hibernate实体之间的关联关系(一对多,多对多)
什么是关联(association)
关联指的是类之间的引用关系。如果类A与类B关联,那么被引用的类B将被定义为类A的属性。
一对一的关系
一对一比较好理解,就是简单的数据表与实体类之间的一对一的映射关系。
举个例子:一个用户对应一个角色(先不考虑复杂情况)
那么在对应的实体类中添加关联:
private Role role;//建立用户与角色之间的连接关系 private User user;//建立用户与角色的一对一关系
在用户配置文件中添加角色的:
<!-- 用户角色一对一关系 --> <one-to-one name="user" class="com.zking.system.entity.User" ></one-to-one>
在角色配置文件中添加用户的:
<!-- 配置一对一关系映射 --> <one-to-one name="role" class="com.zking.system.entity.Role" ></one-to-one>
一对多的关系
一对多一般通过引用集合来实现一个实体与另外的实体集合之间的映射关系。
public class Order implements Serializable { private String username; /** * 实体层面的一对多关系,以用户对应多个订单为例 */ private Set<Order> items = new HashSet<>(); public void setUsername(String username) { this.username= username; } public String getUsername() { return this.username; } public void setItems (Set<Order> Items) { this.Items= Items; } public Set<Order> getItems() { return this.Items; } }
添加关系映射文件:
<hibernate-mapping> <!-- 映射类 --> <class name="com.zking.entity.User" table="t_user_hb"> <!-- 主键 name:类的属性名 id:表的主键列 type:类的主键类型 --> <id name="username" column="username" type="java.lang.String"> <!-- 主键生成器(程序员控制/数据库表控制identity/hibernate控制increment/native包括identity、sequence、hilo) --> <generator class="identity"></generator> </id> <!-- 建立一对多的映射关系 name:对应实体类种的属性名 cascade:级联操作(save-update:级联更新\none:不使用级联\all\delete:级联删除)
inverse:是否由对方控制关联关系 --> <set name="items" cascade="save-update" inverse="true"> <!-- 定义外键指向 --> <key column="oid"></key> <!-- 一对多关系 --> <one-to-many class="com.zking.entity.OrderItem" /> </set> </class> </hibernate-mapping>
Order实体:
/** * 实体层面的多对一的关系 */ private User user; public User getUser() { return user; } public void setUser(User user) { this.user= user; }
<hibernate-mapping> <!-- 映射类 --> <class name="com.zking.entity.Order" table="t_order_hb"> <!-- 主键 name:类的属性名 id:表的主键列 type:类的主键类型 --> <id name="orderId" column="order_id" type="java.lang.Integer"> <generator class="identity"></generator> </id> <property name="productId" column="product_id" type="java.lang.Integer"></property> <property name="quantity" column="quantity" type="java.lang.Integer"></property> <!-- 声明多对一的关系 name:为实体类中的属性 class:对应的类全路径 column:对应关系的数据表列名,实际上就是外键 --> <many-to-one name="user" class="com.zking.entity.User" column="username"> </many-to-one> </class> </hibernate-mapping>
与数据库进行交互:
public void addUser(User u) { //打开会话 Session session = SessionFactoryUtils.openSession(); //打开事务 Transaction transaction = session.beginTransaction(); //添加订单 session.save(u); //提交事务 transaction.commit(); //关闭会话 SessionFactoryUtils.closeSession(); } public static void main(String[] args){ //添加用户同时保存订单 User u = new User(); u.setUsername("No.1"); //添加两个订单 Order o1 = new Order(); o1.setOid("123"); Order o2 = new Order(); o2.setOid("124"); //多对一 o1.setUser(u); o2.setUser(u); //一对多 u.getItems().add(o1); u.getItems().add(o2); //数据交互 new UserDao().addUser(u);
}
多对多的关系
数据库中不能直接映射多对多;处理:创建一个桥接表(中间表),将一个多对多关系转换成两个一对多,hibernate可以直接映射多对多关联关系(看作两个一对多) 。
注意:多对多的关系必须指定一个主控方(inverse=false);主控方直接删除;被控方先通过主控方解除多对多关系,再删除被控方.。
这里以书本与书本类型为例。
//书本实体与书本类型实体分别添加关联关系: //建立实体层面的对应关系:一对多 private Set<Book> books = new HashSet<>(); //建立实体层面的对应关系:一对多 private Set<Category> categories = new HashSet<>();
书本映射文件:
<!-- 映射类 --> <class name="com.zking.entity.Book" table="t_book_hb"> <!-- 主键 name:类的属性名 id:表的主键列 type:类的主键类型 --> <id name="bookId" column="book_id" type="java.lang.Integer"> <generator class="identity"></generator> </id> <property name="bookName" column="book_name" type="java.lang.String"></property> <property name="price" column="price" type="java.lang.Float"></property> <!-- 建立一对多的映射关系 name:对应实体类种的属性名 cascade:级联操作(save-update:级联更新\none:不使用级联\all\delete:级联删除) inverse:是否为主控方,false代表中间表由自己控制,true代表由对方控制 table:用于关联的中间表 --> <set name="categories" lazy="true" cascade="save-update" inverse="false" table="t_book_category_hb"> <!-- 定义中间表的外键指向 --> <key column="bid"></key> <!-- 多对多关系 --> <many-to-many column="cid" class="com.zking.entity.Category" /> </set> </class>
书本类型映射文件:
<!-- 映射类 --> <class name="com.zking.entity.Category" table="t_category_hb"> <!-- 主键 name:类的属性名 id:表的主键列 type:类的主键类型 --> <id name="categoryId" column="category_id" type="java.lang.Integer"> <generator class="identity"></generator> </id> <property name="categoryName" column="category_name" type="java.lang.String"></property> <!-- 建立一对多的映射关系 name:对应实体类种的属性名 cascade:级联操作(save-update:级联更新\none:不使用级联\all\delete:级联删除) table:用于关联的中间表 --> <set name="books" cascade="save-update" inverse="true" table="t_book_category_hb"> <!-- 定义外键指向 --> <key column="cid"></key> <!-- 一对多关系 --> <many-to-many column="bid" class="com.zking.entity.Book" /> </set> </class>
添加书本及书本类型:
Book bk = new Book();
bk.setBookName("一万个为什么"); bk.setPrice(new Float(199.9)); //添加书本类型: Category c1 = new Category(); c1.setCategoryName("悬疑"); Category c2 = new Category(); c2.setCategoryName("科普"); //将书本与书本类型建立联系 bk.getCategories().add(c1); bk.getCategories().add(c2); c1.getBooks().add(bk); c2.getBooks().add(bk); //新增操作 bkdao.addBook(bk);
添加书本以及已有的书本类型:
Book bk = new Book(); bk.setBookName("java编程思想"); bk.setPrice(new Float(999.9)); //添加书本类型: CategoryDao cdao = new CategoryDao(); Category c1 = cdao.getSingle(new Category(1)); //将查询到的书本类型添加到书本对象中,建立联系 bk.getCategories().add(c1); //新增操作 bkdao.addBook(bk);
删除书本或书本类型:
//1:删除主控方Book(inverse=false),由hibernate自己来解除关联关系 bk.setBookId(3); bkdao.deleteBook(bk); //2:删除被控方Category(inverse=true):必须要由手动解除关联,否则hibernate报外键约束错误。 Category c = new Category(8); new CategoryDao().deleteSingle(c); //3:级联删除:将cascade设置为delete,将会删除与之关联的所有表数据(禁用) bk.setBookId(1); bkdao.deleteBook(bk);
总结
1. lazy(hibernate 3.0之后默认延迟加载):默认值为true,true延迟加载,false立即加载(一般设置为true,不使用立即加载,因为影响查询性能)
2. outter-join:默认值为false,true使用左外联接查询关联的(但一般不用,因为当我们把该属性设置为true时,所有的查询语句都会默认左外联,那样性能不高)
3. inverse:默认值为false表示自己为主控方,true表示将对方设置为主控方(一对多双向关联中一般将多方设置为主控方,这样可以减少SQL语句的数量,减少多余的操作)
4. cascade:用来控制如何操作关联的持久化对象的
1). none:保存,更新或删除当前对象时,忽略其它关联的对象
2). save-update:保存、更新时级联保存所有的临时对象,并且级联更新关联的游离对象
3). delete:通过session的delete方法删除当前对象,级联删除关联的对象
4). all:等于save-update操作+delete操作
多方的CRUD与没有建立关联关系之前的操作一致
一方的CRUD与没有建立关联关系之前的操作有变化
Delete:删除一方之前先删除多方
Create:级联操作
Remove:代码控制多方