hibernate关联关系(多对多)
1. 数据库的多对多
1.1 数据库中不能直接映射多对多
处理:创建一个桥接表(中间表),将一个多对多关系转换成两个一对多
注1:数据库多表联接查询
永远就是二个表的联接查询
A B C D
t1 C
t2 D
t3
注2:交叉连接
注3:外连接:left(左)/right(右)/full(左右)
主从表:连接条件不成立时,主表记录永远保留,与null匹配
A B AB
select * from A,B,AB WHERE A.aID=AB.aID and b.bid = AB.bid
where
在hibernate中,你只管查询当前表对象即可,
hibernate会自动关联桥表以及关联表查询出关联对象
Book Category Book_category
select * from Book b,Book_category bc,category where b.bid = bc.bid and bc.cid = c.cid
and bid = 2
2. hibernate的多对多
2.1 hibernate可以直接映射多对多关联关系(看作两个一对多)
3. 多对多关系注意事项
3.1 一定要定义一个主控方
3.2 多对多删除
3.2.1 主控方直接删除
3.2.2 被控方先通过主控方解除多对多关系,再删除被控方
3.2.3 禁用级联删除
3.3 关联关系编辑,不需要直接操作桥接表,hibernate的主控方会自动维护
一对多关联关系案例:
实体类
package com.huang.four.entity; import java.util.HashSet; import java.util.Set; public class TreeNode { private Integer nodeId; private String nodeName; private Integer treeNodeType; private Integer position; private String url; private TreeNode parent; private Set<TreeNode> children = new HashSet<TreeNode>(); private Integer initChildren = 0; public Integer getNodeId() { return nodeId; } public void setNodeId(Integer nodeId) { this.nodeId = nodeId; } public String getNodeName() { return nodeName; } public void setNodeName(String nodeName) { this.nodeName = nodeName; } public Integer getTreeNodeType() { return treeNodeType; } public void setTreeNodeType(Integer treeNodeType) { this.treeNodeType = treeNodeType; } public Integer getPosition() { return position; } public void setPosition(Integer position) { this.position = position; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public TreeNode getParent() { return parent; } public void setParent(TreeNode parent) { this.parent = parent; } public Set<TreeNode> getChildren() { return children; } public void setChildren(Set<TreeNode> children) { this.children = children; } public Integer getInitChildren() { return initChildren; } public void setInitChildren(Integer initChildren) { this.initChildren = initChildren; } @Override public String toString() { return "TreeNode [nodeId=" + nodeId + ", nodeName=" + nodeName + ", treeNodeType=" + treeNodeType + ", position=" + position + ", url=" + url + ", initChildren=" + initChildren + "]"; } }
配置实体映射文件
<?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> <class name="com.huang.four.entity.TreeNode" table="t_hibernate_sys_tree_node"> <id name="nodeId" type="java.lang.Integer" column="tree_node_id"> <generator class="increment" /> </id> <property name="nodeName" type="java.lang.String" column="tree_node_name"> </property> <property name="treeNodeType" type="java.lang.Integer" column="tree_node_type"> </property> <property name="position" type="java.lang.Integer" column="position"> </property> <property name="url" type="java.lang.String" column="url"> </property> <many-to-one name="parent" class="com.huang.four.entity.TreeNode" column="parent_node_id"/> <set name="children" cascade="save-update" inverse="true"> <key column="parent_node_id"></key> <one-to-many class="com.huang.four.entity.TreeNode"/> </set> </class> </hibernate-mapping>
然后用junit测试
TreeNodeDao
package com.huang.four.dao; import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.Transaction; import com.huang.four.entity.TreeNode; import com.huang.two.util.SessionFactoryUtils; public class TreeNodeDao { public TreeNode load(TreeNode treeNode) { Session session = SessionFactoryUtils.openSession(); Transaction transaction = session.beginTransaction(); TreeNode t = session.load(TreeNode.class, treeNode.getNodeId()); if(t != null && new Integer(1).equals(treeNode.getInitChildren())) { Hibernate.initialize(t.getChildren()); Hibernate.initialize(t.getParent()); } transaction.commit(); session.close(); return t; } }
TreeNodeDaoTest
package com.huang.four.dao; import org.junit.Test; import com.huang.four.entity.TreeNode; public class TreeNodeDaoTest { private TreeNodeDao treeNodeDao = new TreeNodeDao(); // @Before // public void setUp() throws Exception { // } // // @After // public void tearDown() throws Exception { // } @Test public void testLoad() { TreeNode treeNode = new TreeNode(); treeNode.setNodeId(6); treeNode.setInitChildren(1); TreeNode t = this.treeNodeDao.load(treeNode); System.out.println(t); System.out.println(t.getParent()); System.out.println(t.getChildren()); } }
运行结果:
Hibernate: select treenode0_.tree_node_id as tree_nod1_6_0_, treenode0_.tree_node_name as tree_nod2_6_0_, treenode0_.tree_node_type as tree_nod3_6_0_, treenode0_.position as position4_6_0_, treenode0_.url as url5_6_0_, treenode0_.parent_node_id as parent_n6_6_0_ from t_hibernate_sys_tree_node treenode0_ where treenode0_.tree_node_id=? Hibernate: select children0_.parent_node_id as parent_n6_6_0_, children0_.tree_node_id as tree_nod1_6_0_, children0_.tree_node_id as tree_nod1_6_1_, children0_.tree_node_name as tree_nod2_6_1_, children0_.tree_node_type as tree_nod3_6_1_, children0_.position as position4_6_1_, children0_.url as url5_6_1_, children0_.parent_node_id as parent_n6_6_1_ from t_hibernate_sys_tree_node children0_ where children0_.parent_node_id=? Hibernate: select treenode0_.tree_node_id as tree_nod1_6_0_, treenode0_.tree_node_name as tree_nod2_6_0_, treenode0_.tree_node_type as tree_nod3_6_0_, treenode0_.position as position4_6_0_, treenode0_.url as url5_6_0_, treenode0_.parent_node_id as parent_n6_6_0_ from t_hibernate_sys_tree_node treenode0_ where treenode0_.tree_node_id=? TreeNode [nodeId=6, nodeName=权限管理, treeNodeType=1, position=6, url=null, initChildren=0] TreeNode [nodeId=1, nodeName=系统管理, treeNodeType=1, position=1, url=null, initChildren=0] [TreeNode [nodeId=10, nodeName=用户分配角色, treeNodeType=2, position=10, url=null, initChildren=0], TreeNode [nodeId=11, nodeName=角色授予用户, treeNodeType=2, position=11, url=null, initChildren=0]]
下面是多对多关联
实体类
Book
package com.huang.four.entity; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Book implements Serializable{ // book_id int primary key auto_increment, // book_name varchar(50) not null, // price float not null private Integer bookId; private String bookName; private Float price; private Set<Category> categories = new HashSet<Category>(); private Integer initCategories = 0; public Integer getInitCategories() { return initCategories; } public void setInitCategories(Integer initCategories) { this.initCategories = initCategories; } public Integer getBookId() { return bookId; } public void setBookId(Integer bookId) { this.bookId = bookId; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public Float getPrice() { return price; } public void setPrice(Float price) { this.price = price; } public Set<Category> getCategories() { return categories; } public void setCategories(Set<Category> categories) { this.categories = categories; } @Override public String toString() { return "Book [bookId=" + bookId + ", bookName=" + bookName + ", price=" + price + "]"; } public Book(Integer bookId, String bookName) { super(); this.bookId = bookId; this.bookName = bookName; } public Book() { super(); } }
实体映射文件
Book.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> <class name="com.huang.four.entity.Book" table="t_hibernate_book"> <cache usage="read-only" region="com.zking.five.entity.Book"/> <id name="bookId" type="java.lang.Integer" column="book_id"> <generator class="increment" /> </id> <property name="bookName" type="java.lang.String" column="book_name"> </property> <property name="price" type="java.lang.Float" column="price"> </property> <!-- set标签: table:对应的是中间表 没有实体类,意味着靠两张主表对应的映射文件联合管理数据 name:当前映射文件对应的实体类对应的属性 cascade:级联新增修改,说白了就是当前实体类对应的表删除能否影响到关联表的数据 inverse:中间表的数据维护的权利交给对方 key标签 column:当前表t_hibernate_book的主键在中间表的列段bid many-to-many: column:代表中间表对应的除去当前表t_hibernate_book的非主键的中间表列段 class:cid对应的类 --> <set table="t_hibernate_book_category" name="categories" cascade="save-update" inverse="true"> <!-- one --> <key column="bid"></key> <!-- many --> <many-to-many column="cid" class="com.huang.four.entity.Category"></many-to-many> </set> </class> </hibernate-mapping>
注意一定要在hibernate.hbm.xml中加入以下代码:
<!-- 多对多关联 --> <mapping resource="com/huang/four/entity/Book.hbm.xml" />
测试:
BookDao
package com.huang.four.dao; import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.Transaction; import com.huang.four.entity.Book; import com.huang.four.entity.Category; import com.huang.two.util.SessionFactoryUtils; public class BookDao{ public Integer addBook(Book book) { Session session = SessionFactoryUtils.openSession(); Transaction transaction = session.beginTransaction(); Integer bid = (Integer) session.save(book); transaction.commit(); session.close(); return bid; } public Integer addCategory(Category category) { Session session = SessionFactoryUtils.openSession(); Transaction transaction = session.beginTransaction(); Integer cid = (Integer) session.save(category); transaction.commit(); session.close(); return cid; } public Category getCategory(Category category) { Session session = SessionFactoryUtils.openSession(); Transaction transaction = session.beginTransaction(); Category c = session.get(Category.class, category.getCategoryId()); transaction.commit(); session.close(); return c; } public Book getBook(Book book) { Session session = SessionFactoryUtils.openSession(); Transaction transaction = session.beginTransaction(); Book b = session.get(Book.class, book.getBookId()); if (b != null && new Integer(1).equals(book.getInitCategories())) { Hibernate.initialize(b.getCategories()); } transaction.commit(); session.close(); return b; } public void delBook(Book book) { Session session = SessionFactoryUtils.openSession(); Transaction transaction = session.beginTransaction(); session.delete(book); transaction.commit(); session.close(); } public void delCategory(Category category) { Session session = SessionFactoryUtils.openSession(); Transaction transaction = session.beginTransaction(); Category c = session.get(Category.class, category.getCategoryId()); if(c!=null) { for (Book b : c.getBooks()) { // 通过在被控方通过主控方来解除关联关系,最后被控方再做删除 b.getCategories().remove(c); } } session.delete(c); transaction.commit(); session.close(); } }
BookDaoTest
package com.huang.four.dao; import org.junit.Test; import com.huang.four.entity.Book; import com.huang.four.entity.Category; public class BookDaoTest { private BookDao bookDao = new BookDao(); @Test public void testGetBook() { Book book = new Book(); book.setBookId(8); book.setInitCategories(1); Book b = this.bookDao.getBook(book ); System.out.println(b.getBookName()); System.out.println(b.getCategories()); } }
结果:
Hibernate: select book0_.book_id as book_id1_0_0_, book0_.book_name as book_nam2_0_0_, book0_.price as price3_0_0_ from t_hibernate_book book0_ where book0_.book_id=? Hibernate: select categories0_.bid as bid1_1_0_, categories0_.cid as cid2_1_0_, category1_.category_id as category1_2_1_, category1_.category_name as category2_2_1_ from t_hibernate_book_category categories0_ inner join t_hibernate_category category1_ on categories0_.cid=category1_.category_id where categories0_.bid=? 圣墟 [Category [categoryId=8, categoryName=言情], Category [categoryId=9, categoryName=言情2]]
book.hbm.xml inverse=fasle
category.hbm.xml inverse=true
数据添加正常
书籍表、桥接表各新增一条数据
package com.huang.four.dao;
import org.junit.Test;
import com.huang.four.entity.Book;
import com.huang.four.entity.Category;
public class BookDaoTest {
private BookDao bookDao = new BookDao();
@Test public void test1() { Book book = new Book(); book.setBookName("T226"); book.setPrice(10f); Category category = new Category(); category.setCategoryId(5); // 直接将category对象加入到新建的book中是错误的,因为此时的category是临时态的,hibernate是不会管理的 // book.getCategories().add(category); Category c = this.bookDao.getCategory(category); // c.getBooks().add(book); book.getCategories().add(c); this.bookDao.addBook(book); }
运行结果:
Hibernate: select category0_.category_id as category1_2_0_, category0_.category_name as category2_2_0_ from t_hibernate_category category0_ where category0_.category_id=? Hibernate: select max(book_id) from t_hibernate_book Hibernate: insert into t_hibernate_book (book_name, price, book_id) values (?, ?, ?) Hibernate: update t_hibernate_category set category_name=? where category_id=?
book.hbm.xml inverse=true
category.hbm.xml inverse=true
只增加书籍表数据
桥接表不加数据
原因:双方都没有去维护关系
package com.huang.four.dao; import org.junit.Test; import com.huang.four.entity.Book; import com.huang.four.entity.Category; public class BookDaoTest { private BookDao bookDao = new BookDao(); @Test public void test2() { Book book = new Book(); book.setBookName("T226hyc"); book.setPrice(10f); Category category = new Category(); category.setCategoryId(5); Category c = this.bookDao.getCategory(category); book.getCategories().add(c); this.bookDao.addBook(book); // c.getBooks().add(book); } }
结果:
Hibernate: select category0_.category_id as category1_2_0_, category0_.category_name as category2_2_0_ from t_hibernate_category category0_ where category0_.category_id=? Hibernate: select max(book_id) from t_hibernate_book Hibernate: insert into t_hibernate_book (book_name, price, book_id) values (?, ?, ?) Hibernate: update t_hibernate_category set category_name=? where category_id=?