Hibernate多对多
Hibernate多对多
数据库的多对多:
数据库中不能直接映射多对多
处理:创建一个桥接表(中间表),将一个多对多关系转换成两个一对多
注:数据库多表联接查询,永远就是二个表的联接查询
hibernate的多对多:
hibernate可以直接映射多对多关联关系(看作两个一对多)
多对多关系注意事项:一定要定义一个主控方
讲解下一对多的自联:
TreeNode.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.xzy.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.xzy.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.xzy.four.entity.TreeNode"/>
</set>
</class>
</hibernate-mapping>
TreeNode.java
package com.xzy.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;
// 0代表懒加载; 1代表强制加载(子节点); 2强制加载用户; 3强制加载两个。
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 + ", children=" + children + "]";
// }
@Override
public String toString() {
return "TreeNode [nodeId=" + nodeId + ", nodeName=" + nodeName + ", treeNodeType=" + treeNodeType
+ ", position=" + position + ", url=" + url + "]";
}
}
TreeNodeDao.java
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;
}
}
@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());
}
案例:
书本表:t_hibernate_book
书本类型表:t_hibernate_category
关联表:t_hibernate_book_category
-- 书本类别表
create table t_hibernate_category
(
category_id int primary key auto_increment,
category_name varchar(50) not null
);
-- 书本表
create table t_hibernate_book
(
book_id int primary key auto_increment,
book_name varchar(50) not null,
price float not null
);
-- 桥接表
-- 定义三个列,其实只要两个列
-- 一个类别对应多本书,一本书对应多个类别
create table t_hibernate_book_category
(
bcid int primary key auto_increment,
bid int not null,
cid int not null,
foreign key(bid) references t_hibernate_book(book_id),
foreign key(cid) references t_hibernate_category(category_id)
);
多对多的配置文件介绍
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.xzy.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>
<!--
table:代表的是中间表
name:书籍类的关联属性
inverse:中间表交于对方维护
key:当前类对应的表列段在中间表(t_hibernate_book_category)的外键(bid)
many-to-many
column:对应的是上面key查出来中间表(t_hibernate_book_category)的另一个字段(cid),当作关联表的主键(category_id)进行查询
class :上述查出来的主键对应的实体类
-->
<set table="t_hibernate_book_category" name="categories" cascade="save-update" inverse="false">
<!-- one -->
<key column="bid"></key>
<!-- many -->
<many-to-many column="cid" class="com.xzy.four.entity.Category"></many-to-many>
</set>
</class>
</hibernate-mapping>
category.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.xzy.four.entity.Category" table="t_hibernate_category">
<id name="categoryId" type="java.lang.Integer" column="category_id">
<generator class="increment" />
</id>
<property name="categoryName" type="java.lang.String"
column="category_name">
</property>
<set table="t_hibernate_book_category" name="books" cascade="save-update" inverse="true">
<key column="cid"></key>
<many-to-many column="bid" class="com.xzy.four.entity.Book"></many-to-many>
</set>
</class>
</hibernate-mapping>
注意:key对应的column不要填反,这里填的是当前类所映射的表的主键,所对应的桥接表中的外键
书本:Book.java
package com.xzy.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();
}
}
书本类型:category.java
package com.xzy.four.entity;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
public class Category implements Serializable{
// category_id int primary key auto_increment,
// category_name varchar(50) not null
private Integer categoryId;
private String categoryName;
private Set<Book> books = new HashSet<Book>();
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public Set<Book> getBooks() {
return books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
@Override
public String toString() {
return "Category [categoryId=" + categoryId + ", categoryName=" + categoryName + "]";
}
}
多对多的查询
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;
}
@Test
public void testGetBook() {
Book book = new Book();
book.setBookId(3);
book.setInitCategories(1);
Book b = this.bookDao.getBook(book );
System.out.println(b.getBookName());
System.out.println(b.getCategories());
}
多对多的新增(inverse属性详解)
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;
}
/**
* book.hbm.xml inverse=fasle
* category.hbm.xml inverse=true
* 数据添加正常
* 书籍表、桥接表各新增一条数据
*/
@Test
public void test1() {
Book book = new Book();
book.setBookName("⭐先打我队友");
book.setPrice(10f);
Category category = new Category();
category.setCategoryId(2);
// 直接将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);
}
/**
* book.hbm.xml inverse=true
* category.hbm.xml inverse=true
* 只增加书籍表数据
* 桥接表不加数据
* 原因:双方都没有去维护关系
*/
@Test
public void test2() {
Book book = new Book();
book.setBookName("哈嘎滑稽");
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);
}
/**
* inverse进一步讲解
*/
@Test
public void test3() {
Category category = new Category();
category.setCategoryName("4");
Book book = new Book();
book.setBookId(3);
Book b = bookDao.getBook(book);
category.getBooks().add(b);
this.bookDao.addCategory(category);
}
cascade="save-update" inverse="false"
cascade级联操作
cascade表示级联操作,即两个实体间存在级联关系(一个类是另一个类中的属性)时,当保存、更新或删除一个实体时,是否对关联的实体做出相应操作(数据操作)。
在hibernate中,通过session.save方法保存一个持久化对象这种方式称为显示保存
inverse标签属性:
inverse:外键维护,默认为false。代表一方不去维护多方外键。(inverse有反转的意思)
外键属于谁, 谁就负责维护.
用于指示本方是否参与维护关系,设置为true表示不维护,设置false表示维护,默认为false。
本属性一般用于一对多关系中一端,并设置为false。因为若由一端负责维护,每次更新完一端数据,都会去寻找与一端有关系的多端表中的行,并更新其外键字段。而多端维护时,由于一端对象是多端对象的属性字段,所以,每次更新多端后提交数据,都会自动更新该字段(若有更新时),这样比较方便。
多对多的级联删除(慎用,会将三张表的数据清空)
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();
}
/**
* 书籍为主控方
*/
@Test
public void test4() {
Book book = new Book();
book.setBookId(1);
this.bookDao.delBook(book);
}
@Test
public void test5() {
Category category = new Category();
category.setCategoryId(1);
this.bookDao.delCategory(category);
}
注意的地方:
非级联删除只会删除正向多方数据库表和中间关系表中的记录,不会删除反向多方表中的记录
级联删除既会删除正向多方数据库表中的记录,也会删除反向多方表中的记录和中间关系表中的记录
。。。。。