您的位置:首页 > 其它

Hibernate一对一双向关联映射以及相互引用toString()方法的错误原因

2016-12-27 14:28 507 查看
问题引入:首先我们搭建hibernate一对一双向关联关系,在我们建立hibernate一对一双向关联之后不能再toString()方法中相互引入,否则会报StackOverFlowError,也就是栈深度不够异常。为什么会出现这种错误,我们先搭建框架和配置关联关系,然后分析错误;我这里使用JDK-1.6和Hibernate-3.2版本,数据库使用Mysql.

一、首先我们搭建hibernate框架

  1.首先建立一个Java工程

  2.然后引入hibernate所需要的jar包和相关数据库连接包

  3.然后创建hibernate,cfg.xml文件(我这里提前把映射文件注入进来了)

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>

<session-factory>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.url">jdbc:mysql://localhost:3306/accp2</property>
<property name="connection.username">root</property>
<property name="connection.password">123</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="myeclipse.connection.profile">mysql</property>

<!-- 自动映射成表 -->
<property name="hbm2ddl.auto">update</property>
<!-- 生成格式化的sql语句 -->
<property name="format_sql">true</property>
<!-- 在控制台打印SQL语句 -->
<property name="show_sql">true</property>

<mapping resource="com/accp/model/IdCard.hbm.xml"/>
<mapping resource="com/accp/model/User.hbm.xml"/>

</session-factory>

</hibernate-configuration>

4.然后创建实体类,这里我用User用户类和IdCard身份信息类来关联,就像一个人只能有一张身份证,一张身份证也只能找到一个名字,正好构成一对一双向关联关系。

    4.1 创建User.java

package com.accp.model;

import java.io.Serializable;

/**
* @author huangdaye
*用户实体类
*/
public class User implements Serializable{

/**
*
*/
private static final long serialVersionUID = 1L;

private Integer id;

private String username;

private String password;

private IdCard idCard;

public User() {
super();
// TODO Auto-generated constructor stub
}

public User(String username, String password) {
super();
this.username = username;
this.password = password;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public IdCard getIdCard() {
return idCard;
}

public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}

@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password="
+ password + ", idCard=" + idCard + "]";
}

}

 4.2创建User.hbm.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 配置用户实体类的映射文件 -->
<hibernate-mapping package="com.accp.model">
<class name="User" table="user">
<id name="id" column="u_id" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<property name="username" column="u_username" type="java.lang.String"/>
<property name="password" column="u_password" type="java.lang.String"/>
<!-- 配置一对一的关联关系, 使用unique属性将多对一转化为一对一-->
<many-to-one name="idCard" column="cid"
class="com.accp.model.IdCard" unique="true"/>
</class>

</hibernate-mapping>

4.3创建Idcard.java

package com.accp.model;

import java.io.Serializable;

/**
* @author huangdaye
*用户身份证实体类
*/
public class IdCard implements Serializable{

/**
*
*/
private static final long serialVersionUID = 1L;

private Integer id;
private String number;
private User user;

public IdCard() {
super();
// TODO Auto-generated constructor stub
}
public IdCard(String number) {
super();
this.number = number;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "IdCard [id=" + id + ", number=" + number + ", user=" + user
+ "]";
}

}

4.4创建IdCard.hbm.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 身份证实体类映射文件 -->
<hibernate-mapping package="com.accp.model">
<class name="IdCard" table="idcard">
<id name="id" column="c_id" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<property name="number" column="c_number" type="java.lang.String"/>

<!-- 配置一对一关联关系 -->
<one-to-one name="user" class="com.accp.model.User" property-ref="idCard"/>
</class>

</hibernate-mapping>

5.建立开启Session的工具类
package com.accp.tools;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Tool {

private static Configuration cfg;
private static SessionFactory sessionFac
4000
tory;

static{
cfg=new Configuration().configure();
sessionFactory=cfg.buildSessionFactory();
}

public Session getSession(){
return sessionFactory.openSession();
}
}


6.建立Test.java测试类
package com.accp.test;

import java.util.Iterator;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.After;
import org.junit.Before;

import com.accp.model.IdCard;
import com.accp.model.User;
import com.accp.tools.Tool;

public class Test {
private Session session;
private Transaction transaction;

@Before
public void before(){
session=new Tool().getSession();
transaction=session.beginTransaction();
}

@After
public void after(){
try {
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
transaction.rollback();
}finally{
session.close();
}
}

@org.junit.Test
public void add(){
User u=new User("leo003", "123");
IdCard ic=new IdCard("430904199412131134");
u.setIdCard(ic);
session.save(ic);
session.save(u);
}

@org.junit.Test
public void query(){
User u=(User) session.load(User.class, 1);
System.out.println("---------------------------------------------------");
System.out.println(u);
System.out.println("---------------------------------------------------");
User u1=(User) session.get(User.class, 1);
System.out.println(u1);
}

public void queryList(){
String hql="select from User";
Query query=session.createQuery(hql);
List<User> list=query.list();
Iterator<User> it=list.iterator();
while (it.hasNext()) {
User user = (User) it.next();
System.out.println(user);
}
}

}

注:这里User类和IdCard类在toString()方法上存在相互调用,为了能直接使用toString()直接输出关联对象的信息

然后执行Test类中的query()方法,这里使用JUnit测试,测试结果如下:



这里我直接截图,可以看出能发出SQL语句,query获取不到对应的对象信息,而且出现了StackOverFlowError的错误,原因在于Java在栈区调用方法的时候出现了死循环,导致一直调用对应对象toString()的方法,然后出现栈内存深度不够,为什么不出现OutOfMemoryError的错误呢?原因在于Java虚拟机内存分布中,栈区是一个储存基本数据和执行方法的区域,而在单线程中,由于内存够用,在出现OutOfMemoryError之前就会出现StackOverFlowError错误。然而在多线程并发中就有可能会出现OutOfMemoryError,因为多个线程同时请求,就会造成栈区的动态扩展内存不够,出现内存溢出的异常。

我们删除其中一个JavaBean中的对应对象的调用,再测试,这里我们删掉toString()方法中IdCard调用

package com.accp.model;

import java.io.Serializable;

/**
* @author huangdaye
*用户身份证实体类
*/
public class IdCard implements Serializable{

/**
*
*/
private static final long serialVersionUID = 1L;

private Integer id;
private String number;
private User user;

public IdCard() {
super();
// TODO Auto-generated constructor stub
}
public IdCard(String number) {
super();
this.number = number;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "IdCard [id=" + id + ", number=" + number + "]";
}

}

然后再在测试类中执行query()方法,看一看出能发出sql语句,而且正确读取到数据库的结果 。






最后,这是我第一次发博客,也是我个人根据自己学习Java虚拟机结合JavaEE项目中的一些问题总结出来的,如果有不对之处,欢迎大家指出!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息