Hibernate一对多单向配置详细说明实例教程inverse属性用法

Hibernate一对多单向配置详细说明实例教程,index.jsp页面显示:

Hibernate一对多关联,例如一个用户有多张银行卡(只考虑用户到银行卡的单向一对多关联)。由于是学习Hibernate原理,并没有使用工具自动生成代码等。

单向一对多关联只需要在”一方”进行配置即可,”多方”无需额外配置。

a.Java程序中所要做的一对多:

 

双击代码全选
1
2
3
4
5
6
7
8
9
public class User{
    ...
    private Set<Card> cards;
    ...
}
public class Card{
    ...
}

 

b.Hibernate中所要做的一对多:

 

双击代码全选
1
2
3
4
5
6
7
User.hbm.xml:
...
<set name="cards" inverse="false" cascade="all">
    <key column="userId" />
    <one-to-many class="wintys.hibernate.onetomany.Card" />
</set>
...

 

c.数据库中的一对多:

而对应的数据库中,只要相应在Card对应的物理表中添加外键userId(不为设为NOT NULL)即可。

  图片看不清楚?请点击这里查看原图(大图)。

详细的MyEclipse WebProject如下:

1、实体类:

用户类User.java:

 

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package wintys.hibernate.onetomany;
import java.util.Set;
public class User {
    private String id;
    private String name;
    private Set<Card> cards;
   
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setCards(Set<Card> cards) {
        this.cards = cards;
    }
    public Set<Card> getCards() {
        return cards;
    }  
}

 

银行卡类Card.java:

 

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package wintys.hibernate.onetomany;
public class Card {
    private String id;
    private float balance;
   
    public Card(){
    }
    public Card(float balance){
        this.balance = balance;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public float getBalance() {
        return balance;
    }
    public void setBalance(float balance) {
        this.balance = balance;
    }  
}

 

2、数据库表:

数据库是MySQL 5.1.34-community。

用户表:

 

双击代码全选
1
2
3
4
5
CREATE TABLE myuser(
    id               VARCHAR(50)  NOT NULL,
    name         VARCHAR(100),
    PRIMARY KEY(id)
);

 

银行卡表:

 

双击代码全选
1
2
3
4
5
6
CREATE TABLE mycard(
    id               VARCHAR(50)  NOT NULL,
    balance      FLOAT(7,2),
    userId       VARCHAR(50),
    PRIMARY KEY(id)
);

 

3、映射文件:

用户类映射文件/src/wintys/hibernate/onetomany/User.hbm.xml:

 

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
<!--
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="wintys.hibernate.onetomany.User" table="myuser" catalog="db">
        <id name="id" type="string">
            <column name="id" not-null="true"/>
            <generator class="uuid.hex" />
        </id>
        <property name="name" type="java.lang.String" column="name" />
       
        <set name="cards" inverse="false" cascade="all">
            <key column="userId" />
            <one-to-many class="wintys.hibernate.onetomany.Card" />
        </set>
    </class>
</hibernate-mapping>

银行卡映射文件/src/wintys/hibernate/onetomany/User.hbm.xml:

 

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
<!--
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="wintys.hibernate.onetomany.Card" table="mycard" catalog="db">
        <id name="id" type="string">
            <column name="id" not-null="true"/>
            <generator class="uuid.hex" />
        </id>
        <property name="balance" />
    </class>
</hibernate-mapping>

 

Hibernate配置文件:/src/hibernate.cfg.xml:

 

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
<!-- Generated by MyEclipse Hibernate Tools.                   -->
<hibernate-configuration>
<session-factory>
    <property name="connection.username">root</property>
    <property name="connection.url">
    </property>
    <property name="dialect">
        org.hibernate.dialect.MySQLDialect
    </property>
    <property name="myeclipse.connection.profile">MySQLDriver</property>
    <property name="connection.password">root</property>
    <property name="connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <property name="show_sql">true</property>
    <mapping resource="wintys/hibernate/onetomany/User.hbm.xml" />
    <mapping resource="wintys/hibernate/onetomany/Card.hbm.xml" />
</session-factory>
</hibernate-configuration>

 

4、使用测试:

 

双击代码全选
1
2
3
4
5
6
7
8
9
/src/wintys/hibernate/onetomany/HibernateDAO.java:
package wintys.hibernate.onetomany;
import java.util.List;
public interface HibernateDAO {
    public void insert();
    public List<User> selectAll();
}

 

/src/wintys/hibernate/onetomany/HibernateDAOBean.java:

 

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package wintys.hibernate.onetomany;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class HibernateDAOBean implements HibernateDAO {
    public void insert() throws HibernateException {
        Transaction tc = null;
        try{
            Set<Card> cards = new HashSet<Card>();
            Card c1,c2,c3;
            c1 = new Card(7641.96f);
            c2 = new Card(654.8f);
            c3 = new Card(3650f);
           
            cards.add(c1);
            cards.add(c2);
            cards.add(c3);
           
            User user = new User();
            user.setName("Tom");
            user.setCards(cards);
           
            Session session = HibernateUtil.getSession();
            tc = session.beginTransaction();
                       
            /*
            配置文件中的cascade="true"时,所以无需手动保存c1,c2,c3
            session.save(c1);
            session.save(c2);
            session.save(c3);
            */
            session.save(user);
           
            tc.commit();
        }catch(HibernateException e){
            try{
                if(tc != null)
                    tc.rollback();
            }catch(Exception ex){
                System.err.println(ex.getMessage());
            }
            System.err.println(e.getMessage());
        }finally{
            HibernateUtil.closeSession();          
        }
    }
    @SuppressWarnings("unchecked")
    public List<User> selectAll() throws HibernateException {
        List<User> users = null;
        Transaction tc = null;
        try{
            Session session = HibernateUtil.getSession();
            tc = session.beginTransaction();
                       
            Query query = session.createQuery("from User");
            users = query.list();
           
            tc.commit();
        }catch(HibernateException e){
            try{
                if(tc != null){
                    tc.rollback();
                    users = null;
                }
            }catch(Exception ex){
                System.err.println(ex.getMessage());
            }
            System.err.println(e.getMessage());
        }finally{
            //HibernateUtil.closeSession();        
        }
       
        return users;
    }
}

/src/wintys/hibernate/onetomany/HibernateUtil.java:

 

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package wintys.hibernate.onetomany;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
 * Hibernate Session管理
 * @author Winty
 */
public class HibernateUtil {
    private static SessionFactory factory = null;
    private static ThreadLocal<Session> threadLocal;
       
    static {
        try{
            factory = new Configuration()
                    .configure()
                    .buildSessionFactory();
        }catch(HibernateException e){
            System.err.println(e.getMessage());
        }
       
        threadLocal = new ThreadLocal<Session>();
    }
   
    private HibernateUtil(){   
    }
   
    public static Session getSession()throws HibernateException{
        Session session = threadLocal.get();
        if(session == null){
            session = factory.openSession();
            threadLocal.set(session);
        }
       
        return session;
    }
   
    public static void closeSession()throws HibernateException{
        Session session = threadLocal.get();
        if(session != null){
            session.close();
        }
        threadLocal.set(null);
    }
}

 

/index.jsp:

 

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ page import="wintys.hibernate.onetomany.*"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>
 
  <body>
    <%
        List<User> users = null;
        HibernateDAO dao = new HibernateDAOBean();
        dao.insert();
        users = dao.selectAll();
       
        Iterator<User> it = users.iterator();
        while(it.hasNext()){
            User user = it.next();
            String id = user.getId();
            String name = user.getName();
            out.println("id:" + id + "<br />");
            out.println("name:" + name + "<br />");
            out.println("cards:<br />");
           
            Set<Card> cards = user.getCards();
            Iterator<Card> itc = cards.iterator();
            while(itc.hasNext()){
                Card card = itc.next();
                String cardId = card.getId();
                float balance = card.getBalance();
                out.println("&nbsp;&nbsp;&nbsp; cardId:" + cardId + "<br />");
                out.println("&nbsp;&nbsp;&nbsp; balance:" + balance + "<br />");
            }
            out.println("<hr/>");
        }
   
     %>
  </body>
</html>

 

5、运行结果:

控制台显示:

 

双击代码全选
1
2
3
4
5
6
7
8
9
......
Hibernate: insert into db.myuser (name, id) values (?, ?)
Hibernate: insert into db.mycard (balance, id) values (?, ?)
Hibernate: insert into db.mycard (balance, id) values (?, ?)
Hibernate: insert into db.mycard (balance, id) values (?, ?)
Hibernate: update db.mycard set userId=? where id=?
Hibernate: update db.mycard set userId=? where id=?
Hibernate: update db.mycard set userId=? where id=?
......

id:402881e421d4d0be0121d4d20e140005
name:Tom
cards:
cardId:402881e421d4d0be0121d4d20e230008
balance:654.8
cardId:402881e421d4d0be0121d4d20e230006
balance:7641.96
cardId:402881e421d4d0be0121d4d20e230007
balance:3650.0

6、Hibernate一对多单向配置注意的问题:

a、hibernate错误提示:Field ‘userId’ doesn’t have a default value解决方法。

刚开始把”userId”设成NOT NULL,但是Hibernate先执行的是:

“insert into db.mycard (balance, id) values (?, ?)”

然后再执行”update db.mycard set userId=? where id=?”,

但userId在insert时没有写入值的,因此就会报错。这时将userId的NOT NULL去掉就可以了。

b、User.hbm.xml要设置cascade=”all”,或者是其它有效值,要不,保存User对象的时候,有关的Card对象不会被保存。

c、User.hbm.xml中set标签的inverse属性不能设为”true”,inverse的默认为”false”,因此不加inverse也行的。看书上说:在一对多的关联关系实现中,最好设置inverse=”true”,将有助于性能的改善。所以一开始就用了inverse=”true”,User和Card对象都分别正确写入数据库了,但是就是userId字段没有被自动写入。

myuser表:

+——————————————–+——+

| id                                 | name |

+——————————————–+——+

| 402881e421d4d0be0121d4d20e140005  | Tom  |

+——————————————–+——+

mycard表:

+——————————————–+———+———

| id                                | balance | userId

+——————————————–+———+———

| 402881e421d4d0be0121d4d20e230006  | 7641.96 | NULL

| 402881e421d4d0be0121d4d20e230007  | 3650.00 | NULL

|

| 402881e421d4d0be0121d4d20e230008  |  654.80  | NULL

+——————————————–+———+———

问题的原因原来在本例应该把inverse设置为false。inverse是很有用的,不过用错了地方。 本文链接地址: Hibernate一对多单向配置详细说明实例教程inverse属性用法