级联添加
User.hbm.xml设置级联删除,但是没有反转
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- package:实体类的包名 -->
<hibernate-mapping package="com.tamakiakoo.entity">
<!--
name:类名
table:实体类对应的表明
-->
<class name="User" table="t_user" lazy="false">
<!-- 主键要用<id>标签来修饰 -->
<!--
name:对象中的主键名称
column:表中的主键名称(如果表中的字段和对象中的属性一直,column可以不写)
-->
<id name="id" column="id">
<generator class="native" />
</id>
<property name="username" />
<property name="password" />
<property name="email" />
<property name="sex" />
<!--
cascade:级联属性
1:save-update:保存一的一方多的一方可以自动保存
2:delete:删除以的一方多的一方可以自动删除
3:all=save-update+delete
-->
<!-- inverse="true" cascade="all -->
<set name="addSet"">
<key column="user_id" ></key>
<one-to-many class="Address" />
</set>
</class>
</hibernate-mapping>
Address.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.tamakiakoo.entity">
<class name="Address" table="t_address" >
<id name="id">
<generator class="native" />
</id>
<property name="address"/>
<property name="phone" />
<many-to-one name="user">
<column name="user_id"></column>
</many-to-one>
</class>
</hibernate-mapping>
测试一:
User设置级联,user维护关系,进行级联操作
修改user.hbm.xml
<set name="addSet" cascade="all">
<key column="user_id" ></key>
<one-to-many class="Address" />
</set>
代码
@Test
public void testAdd1() {
// a.创建一个用户
User user = new User("admin", "admin");
// b.创建两个地址
Address a1 = new Address("深圳", "123");
Address a2 = new Address("北京", "456");
// 2.维护关系【给以的一方维护关系】目的:就保存一的时候多可以自动保存
user.getAddSet().add(a1);
user.getAddSet().add(a2);
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
session.save(user);
transaction.commit();
}
控制台
Hibernate: insert into t_user (username, password, email, sex) values (?, ?, ?, ?)
Hibernate: insert into t_address (address, phone, user_id) values (?, ?, ?)
Hibernate: insert into t_address (address, phone, user_id) values (?, ?, ?)
Hibernate: update t_address set user_id=? where id=?
Hibernate: update t_address set user_id=? where id=?
数据都正常,address表外键也加进去了
继续测试配置不变
注释掉保存的User,但是因为address没有设置级联而报错
@Test
public void testAdd2() {
// a.创建一个用户
User user = new User("admin", "admin");
// b.创建两个地址
Address a1 = new Address("深圳", "123");
Address a2 = new Address("北京", "456");
a1.setUser(user);
a2.setUser(user);
// 3.入库
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
//session.save(user);
session.save(a1);
session.save(a2);
transaction.commit();
}
控制台
报错:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.tamakiakoo.entity.User
这是因为你的对象引用了一个未保存的瞬态实例,换句话讲就是:因为主表的记录不曾插入,所以参照该记录的从表记录也就无法插入
通过cascade=CascadeType.All将Hibernate的所有持久化操作都级联到关联实体
解决办法一:
修改代码如下:
主要是设置了他的id为1(数据库中存在)
@Test
public void testAdd2() {
// a.创建一个用户
User user = new User("admin", "admin");//
user.setId(1);
// b.创建两个地址
Address a1 = new Address("深圳", "123");
Address a2 = new Address("北京", "456");
a1.setUser(user);
a2.setUser(user);
// 3.入库
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
//session.save(user);
session.save(a1);
session.save(a2);
transaction.commit();
}
}
执行代码之后:数据正常,而且尽管user对象的usernaem和password和数据库不一致也没有修改
解决办法二
在保存地址之前先保存User对象
@Test
public void testAdd2() {
// a.创建一个用户
User user = new User("admin", "admin");
//user.setId(2);
// b.创建两个地址
Address a1 = new Address("深圳", "123");
Address a2 = new Address("北京", "456");
a1.setUser(user);
a2.setUser(user);
// 3.入库
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
session.save(user);
session.save(a1);
session.save(a2);
transaction.commit();
}
数据正常
解决办法三
设置级联操作
修改address.hbm.xlm
<many-to-one name="user" cascade="all">
<column name="user_id"></column>
</many-to-one>
执行代码
@Test
public void testAdd3() {
// a.创建一个用户
User user = new User("admin1", "admin1");
// b.创建两个地址
Address a1 = new Address("深圳", "123");
Address a2 = new Address("北京", "456");
a1.setUser(user);
a2.setUser(user);
// 3.入库
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
//session.save(user);
session.save(a1);
session.save(a2);
transaction.commit();
}
控制台
Hibernate: insert into t_user (username, password, email, sex) values (?, ?, ?, ?)
Hibernate: insert into t_address (address, phone, user_id) values (?, ?, ?)
Hibernate: insert into t_address (address, phone, user_id) values (?, ?, ?)
数据正常
问题:
级联保存user的时候会多出sql
级联保存address的时候要保存多个地址
所以有没有办法保存user的时候不多出sql,并且级联保存address对象呢
如下:
user.hbm.xml 设置反转
<set name="addSet" cascade="all" inverse="true">
<key column="user_id" ></key>
<one-to-many class="Address" />
</set>
address.hbm.xml
<many-to-one name="user">
<column name="user_id"></column>
</many-to-one>
执行代码(user维护关系)
@Test
public void testAdd1() {
// a.创建一个用户
User user = new User("admin", "admin");
// b.创建两个地址
Address a1 = new Address("深圳", "123");
Address a2 = new Address("北京", "456");
// 2.维护关系【给以的一方维护关系】目的:就保存一的时候多可以自动保存
user.getAddSet().add(a1);
user.getAddSet().add(a2);
// 3.入库
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
session.save(user);
transaction.commit();
}
控制台
Hibernate: insert into t_user (username, password, email, sex) values (?, ?, ?, ?)
Hibernate: insert into t_address (address, phone, user_id) values (?, ?, ?)
Hibernate: insert into t_address (address, phone, user_id) values (?, ?, ?)
数据不正常,address表的userid是null
说明user维护了关系是可以级联添加的,只是inverse了address表的userid当然是null,所以需要在级联保存之前 address维护关系
如下:
user.hbm.xml 设置反转
<set name="addSet" cascade="all" inverse="true">
<key column="user_id" ></key>
<one-to-many class="Address" />
</set>
address.hbm.xml
<many-to-one name="user">
<column name="user_id"></column>
</many-to-one>
执行代码(address维护关系)
@Test
public void testAdd1() {
// a.创建一个用户
User user = new User("admin", "admin");
// b.创建两个地址
Address a1 = new Address("深圳", "123");
Address a2 = new Address("北京", "456");
// 维护关系【给多的一方维护关闭】目的:关联字段的维护权在多的一方
a1.setUser(user);
a2.setUser(user);
// 3.入库
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
session.save(user);
transaction.commit();
}
控制台
Hibernate: insert into t_user (username, password, email, sex) values (?, ?, ?, ?)
只保存了user对象,并没有级联到
说明:只有维护了关系才会级联
解决办法
/**
* 保存一的一方多的一方可以自动保存
*/
@Test
public void testAdd1() {
// 1.造数据
// a.创建一个用户
User user = new User("admin", "admin");
// b.创建两个地址
Address a1 = new Address("深圳", "123");
Address a2 = new Address("北京", "456");
// 2.维护关系【给以的一方维护关系】目的:就保存一的时候多可以自动保存
user.getAddresses().add(a1);
user.getAddresses().add(a2);
// 维护关系【给多的一方维护关闭】目的:关联字段的维护权在多的一方
a1.setUser(user);
a2.setUser(user);
// 3.入库
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
session.save(user);
// session.save(a1);
// session.save(a2);
transaction.commit();
}
这样就可以数据正常 而且不会产生对于的SQL语句
级联删除
@Test
public void testAdd4() {
// 3.入库
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
session.delete(session.get(User.class, 182));
transaction.commit();
}
控制台
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.email as email0_0_, user0_.sex as sex0_0_ from t_user user0_ where user0_.id=?
Hibernate: select addset0_.user_id as user4_0_1_, addset0_.id as id1_, addset0_.id as id1_0_, addset0_.address as address1_0_, addset0_.phone as phone1_0_, addset0_.user_id as user4_1_0_ from t_address addset0_ where addset0_.user_id=?
Hibernate: delete from t_address where id=?
Hibernate: delete from t_address where id=?
Hibernate: delete from t_user where id=?
多对多
老师和班级是多对多的关系
多对多的关系产生一张中间表,使用组合外键确定外键
创建表和实体类
package com.tamakiakoo.entity;
import java.util.HashSet;
import java.util.Set;
public class Teacher {
private Integer id;
private String tname;
private Set<Classes> classes = new HashSet<>();
public Teacher(){}
public Teacher(String tname) {
this.tname=tname;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public Set<Classes> getClasses() {
return classes;
}
public void setClasses(Set<Classes> classes) {
this.classes = classes;
}
}
package com.tamakiakoo.entity;
import java.util.HashSet;
import java.util.Set;
public class Classes {
private Integer id;
private String cname;
private Set<Teacher> teachers = new HashSet<>();
public Classes(){}
public Classes(String cname) {
super();
this.cname = cname;
}
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
}
表
映射文件
Theacher.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- package:实体类的包名 -->
<hibernate-mapping package="com.tamakiakoo.entity">
<class name="Teacher" table="t_teacher">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="tname" />
<set name="classes" table="t_classesteacher">
<key column="tid"></key>
<many-to-many class="Classes" column="cid" />
</set>
</class>
</hibernate-mapping>
Classes.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- package:实体类的包名 -->
<hibernate-mapping package="com.tamakiakoo.entity">
<class name="Classes" table="t_classes">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="cname" />
<set name="teachers" table="t_classteacher">
<key column="cid" />
<many-to-many class="Teacher" column="tid"></many-to-many>
</set>
</class>
</hibernate-mapping>
环境测试
@Test
public void testAdd() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
Teacher teacher = new Teacher("达老师");
Classes c1 = new Classes("Java_1901");
session.save(teacher);
session.save(c1);
transaction.commit();
}
遇到如下错误
原因:一般都是配置文件错误,修改配置文件就好了
插入
@Test
public void testAdd2() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
// 1.0 创建两个老师
Teacher t1 = new Teacher("达老师");
Teacher t2 = new Teacher("孙老师");
// 1.1 创建两个班级
Classes c1 = new Classes("Java_1901");
Classes c2 = new Classes("Java_1902");
t1.getClasses().add(c1);
t1.getClasses().add(c2);
t2.getClasses().add(c1);
t2.getClasses().add(c2);
session.save(t1);
session.save(t2);
session.save(c1);
session.save(c2);
transaction.commit();
}
控制台
Hibernate: insert into t_teacher (tname) values (?)
Hibernate: insert into t_teacher (tname) values (?)
Hibernate: insert into t_classes (cname) values (?)
Hibernate: insert into t_classes (cname) values (?)
Hibernate: insert into t_classesteacher (tid, cid) values (?, ?)
Hibernate: insert into t_classesteacher (tid, cid) values (?, ?)
Hibernate: insert into t_classesteacher (tid, cid) values (?, ?)
Hibernate: insert into t_classesteacher (tid, cid) values (?, ?)
数据都正常
如果只保存 session.save(t1); 和 session.save(t2); 就会会出异常
Hibernate: insert into t_teacher (tname) values (?)
Hibernate: insert into t_teacher (tname) values (?)
Hibernate: insert into t_classesteacher (tid, cid) values (?, ?)
error:"object references an unsaved transient instance - save the transient instance before flushing: com.tamakiakoo.entity.Classes"
级联保存
修改Teacher.hbm.xml配置文件
<set name="classes" table="t_classesteacher" cascade="all">
<key column="tid"></key>
<many-to-many class="Classes" column="cid" />
</set>
/**
* 级联保存
*/
@Test
public void testAdd3() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
// 1.造数据
// 1.0 创建两个老师
Teacher t1 = new Teacher("达老师");
Teacher t2 = new Teacher("孙老师");
// 1.1 创建两个班级
Classes c1 = new Classes("Java_1901");
Classes c2 = new Classes("Java_1902");
// 2.维护关系
t1.getClasses().add(c1);
t1.getClasses().add(c2);
t2.getClasses().add(c1);
t2.getClasses().add(c2);
/*
c1.getTeacheres().add(t1);
c1.getTeacheres().add(t2);
c2.getTeacheres().add(t1);
c2.getTeacheres().add(t2);
*/
// 3.保存
session.save(t1);
session.save(t2);
// session.save(c1);
// session.save(c2);
transaction.commit();
}
控制台
Hibernate: insert into t_teacher (tname) values (?)
Hibernate: insert into t_classes (cname) values (?)
Hibernate: insert into t_classes (cname) values (?)
Hibernate: insert into t_teacher (tname) values (?)
Hibernate: insert into t_classesteacher (tid, cid) values (?, ?)
Hibernate: insert into t_classesteacher (tid, cid) values (?, ?)
Hibernate: insert into t_classesteacher (tid, cid) values (?, ?)
Hibernate: insert into t_classesteacher (tid, cid) values (?, ?)
数据正常
Fetch与lazy的配合使用
测试代码
@Test
public void testLoad1() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
User user = (User) session.load(User.class, 179);
System.out.println(user.getUsername());
System.out.println("===============");
System.out.println(user.getAddSet().size());
transaction.commit();
}
测试一:
<set name="addresses" fetch="subselect" lazy="extra">
<!-- 关联外键 -->
<key column="user_id"></key>
<one-to-many class="Address"/>
</set>
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.email as email0_0_, user0_.sex as sex0_0_ from t_user user0_ where user0_.id=?
admin
===============
Hibernate: select count(id) from t_address where user_id =?
2
极其懒惰的情况下:在使用的时候只查询使用的那一部分
测试二:
<set name="addSet" cascade="all" fetch="select" lazy="true">
<key column="user_id" ></key>
<one-to-many class="Address" />
</set>
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.email as email0_0_, user0_.sex as sex0_0_ from t_user user0_ where user0_.id=?
admin
===============
Hibernate: select addset0_.user_id as user4_0_1_, addset0_.id as id1_, addset0_.id as id1_0_, addset0_.address as address1_0_, addset0_.phone as phone1_0_, addset0_.user_id as user4_1_0_ from t_address addset0_ where addset0_.user_id=?
2
懒惰的情况下:在使用的时候才查询
测试三:
<set name="addSet" cascade="all" fetch="select" lazy="false">
<key column="user_id" ></key>
<one-to-many class="Address" />
</set>
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.email as email0_0_, user0_.sex as sex0_0_ from t_user user0_ where user0_.id=?
Hibernate: select addset0_.user_id as user4_0_1_, addset0_.id as id1_, addset0_.id as id1_0_, addset0_.address as address1_0_, addset0_.phone as phone1_0_, addset0_.user_id as user4_1_0_ from t_address addset0_ where addset0_.user_id=?
admin
===============
2
不懒惰 还没有查询的时候就已经查询好了
总结
- fetch:select
- lazy:true:地址使用到的时候才会发送sql语句查询
- lazy:false,地址不适用也会查询
- lazy:extra,:地址使用到的时候才会发送sql语句查询,统计数量的时候用count查询
- fetch:join
- 由于join语法本身的原因,lazy属性设置无效
- subselect
- 用get和load查询结果和select的一样
- 在一定的场景下才可以看出select和subselect的区别,用hql查询
batch-size
<!-- 一次查询2个用户的地址 -->
<set name="addresses" batch-size="2">
<key column="user_id"></key>
<one-to-many class="Address" />
</set>
测试代码
@Test
public void testGetUserList() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = session.beginTransaction();
Query query = session.createQuery("from User"); // hql
List<User> userList= query.list();
for (User user : userList) {
System.out.println(user.getUsername()+":"+user.getAddSet().size());
}
transaction.commit();
}
Hibernate: select user0_.id as id0_, user0_.username as username0_, user0_.password as password0_, user0_.email as email0_, user0_.sex as sex0_ from t_user user0_
上面是查询所有用户的
Hibernate: select addset0_.user_id as user4_0_1_, addset0_.id as id1_, addset0_.id as id1_0_, addset0_.address as address1_0_, addset0_.phone as phone1_0_, addset0_.user_id as user4_1_0_ from t_address addset0_ where addset0_.user_id in (?, ?)
admin1:0
Hibernate: select addset0_.user_id as user4_0_1_, addset0_.id as id1_, addset0_.id as id1_0_, addset0_.address as address1_0_, addset0_.phone as phone1_0_, addset0_.user_id as user4_1_0_ from t_address addset0_ where addset0_.user_id in (?, ?)
admin:0
Hibernate: select addset0_.user_id as user4_0_1_, addset0_.id as id1_, addset0_.id as id1_0_, addset0_.address as address1_0_, addset0_.phone as phone1_0_, addset0_.user_id as user4_1_0_ from t_address addset0_ where addset0_.user_id in (?, ?)
admin:0
admin:0
admin:2
Hibernate: select addset0_.user_id as user4_0_1_, addset0_.id as id1_, addset0_.id as id1_0_, addset0_.address as address1_0_, addset0_.phone as phone1_0_, addset0_.user_id as user4_1_0_ from t_address addset0_ where addset0_.user_id in (?, ?)
admin:0
admin:0
赵莉:0
一次查询2个地址,数据库里面有8个地址 需要查询4次
上面是1-1-3-3 我猜测是输出快慢的原因
HQL查询
-
HQL(Hibernate Query Language)提供了丰富灵活的查询方式,使用HQL进行查询也是Hibernate官方推荐使用的查询方式。
-
HQL在语法结构上和SQL语句十分的相同,所以可以很快的上手进行使用。使用HQL需要用到Hibernate中的Query对象,该对象专门执行HQL方式的操作
查询所有示例
session.beginTransaction();
String hql = "from User"; // from 后跟的是要查询的对象,而不是表
Query query = session.createQuery(hql);
List<User> userList = query.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();
带where的查询示例
session.beginTransaction();
String hql = "from User where userName = 'James'";
Query query = session.createQuery(hql);
List<User> userList = query.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();
/*
在HQL中where语句中使用的是持久化对象的属性名,如上面示例中的userName。当然在HQL中也可以使用别名
*/
String hql = "from User as u where u.userName = 'James'";
/*
过滤条件
在where语句中还可以使用各种过滤条件,如:=、<>、<、>、>=、<=、between、not between、
in、not in、is、like、and、or等
*/
获取一个不完整的对象
session.beginTransaction();
String hql = "select userName from User";
Query query = session.createQuery(hql);
List<Object> nameList = query.list();
for(Object obj:nameList){
String name=(String)obj;
System.out.println(name);
}
session.getTransaction().commit();
// 多个属性的话,需要用object[]接收
session.beginTransaction();
String hql = "select userName,userPwd from User";
Query query = session.createQuery(hql);
List nameList = query.list();
for(Object obj:nameList){
Object[] array = (Object[]) obj; // 转成object[]
System.out.println("name:" + array[0]);
System.out.println("pwd:" + array[1]);
}
session.getTransaction().commit();
统计和分组查询
session.beginTransaction();
String hql = "select count(*),max(id) from User";
Query query = session.createQuery(hql);
List nameList = query.list();
for(Object obj:nameList){
Object[] array = (Object[]) obj;
System.out.println("count:" + array[0]);
System.out.println("max:" + array[1]);
}
session.getTransaction().commit();
/*
该条sql语句返回的是单条数据,所以还可以这样写
单列数据用Object,多列数据用Object[]
*/
Object[] object = (Object[]) query.uniqueResult();
System.out.println("count:" + object[0]);
System.out.println("max:" + object[1]);
更多写法
select distinct name from Student;
select max(age) from Student;
select count(age),age from Student group by age;
from Student order by age;
HQL占位符
session.beginTransaction();
String hql = "from User where userName = ?";
Query query = session.createQuery(hql);
// 索引从0开始
query.setString(0, "James");
List<User> userList = query.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();
HQL引用占位符
session.beginTransaction();
String hql = "from User where userName = :name";
Query query = session.createQuery(hql);
query.setParameter("name", "James");
List<User> userList = query.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();
HQL分页
session.beginTransaction();
String hql = "from User";
Query query = session.createQuery(hql);
query.setFirstResult(0);
query.setMaxResults(2);
List<User> userList = query.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();