一对一映射
设计实体类
idCard.java
public class User{
private Integer userId;
private String userName;
private IdCard idCard;
}
复制代码
idCard.java
public class IdCard{
private Integer idCardId;
private String idCardName;
private User user;
}
复制代码
User.hbm.xml
<?xml version="1.0"?>
<!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.bjlemon.hibernate.entity.User" table="USER">
<id name="userId" type="java.lang.Integer">
<column name="USER_ID" />
<generator class="identity" />
</id>
<property name="userName" type="java.lang.String">
<column name="USER_NAME" />
</property>
<one-to-one name="idCard" class="com.bjlemon.hibernate.entity.IdCard" property-ref="user"></one-to-one>
</class>
</hibernate-mapping>
复制代码
IdCard.hbm.xml
<?xml version="1.0"?>
<!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.bjlemon.hibernate.entity.IdCard" table="IDCARD">
<id name="idCardId" type="java.lang.Integer">
<column name="IDCARD_ID" />
<generator class="identity" />
</id>
<property name="idCardName" type="java.lang.String">
<column name="IDCARD_NAME" />
</property>
<many-to-one name="user"
class="com.bjlemon.hibernate.entity.User" column="user_id" unique="true"></many-to-one>
</class>
</hibernate-mapping>
复制代码
User.hbm.xml
<?xml version="1.0"?>
<!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.bjlemon.hibernate.entity.User" table="USER">
<id name="userId" type="java.lang.Integer">
<column name="USER_ID" />
<generator class="identity" />
</id>
<property name="userName" type="java.lang.String">
<column name="USER_NAME" />
</property>
<one-to-one name="idCard" class="com.bjlemon.hibernate.entity.IdCard"></one-to-one>
</class>
</hibernate-mapping>
复制代码
IdCard.hbm.xml
<?xml version="1.0"?>
<!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.bjlemon.hibernate.entity.IdCard" table="IDCARD">
<id name="idCardId" type="java.lang.Integer">
<column name="IDCARD_ID" />
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<property name="idCardName" type="java.lang.String">
<column name="IDCARD_NAME" />
</property>
<one-to-one name="user"
class="com.bjlemon.hibernate.entity.User" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
复制代码
一对多和多对一映射
一个部门有多个员工:一对多
多个员工属于一个部门:多对一
Dept.java
public class Department {
private Integer deptId;
private String deptName;
private Set<Employee> employees = new HashSet<>();
}
复制代码
Employee.java
public class Employee {
private Integer empId;
private String empName;
private Double empSalary;
private Department department;
}
复制代码
Department.hbm.xml
<?xml version="1.0"?>
<!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.bjlemon.hibernate.entity.Department" table="DEPARTMENT">
<id name="deptId" type="java.lang.Integer">
<column name="DEPT_ID" />
<generator class="native" />
</id>
<property name="deptName" type="java.lang.String">
<column name="DEPT_NAME" />
</property>
<set name="employees" table="EMPLOYEE" inverse="true">
<key>
<column name="DEPT_ID" />
</key>
<one-to-many class="com.bjlemon.hibernate.entity.Employee" />
</set>
</class>
</hibernate-mapping>
复制代码
Employee.hbm.xml
<?xml version="1.0"?>
<!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.bjlemon.hibernate.entity.Employee" table="EMPLOYEE">
<id name="empId" type="java.lang.Integer">
<column name="EMP_ID" />
<generator class="native" />
</id>
<property name="empName" type="java.lang.String">
<column name="EMP_NAME" />
</property>
<property name="empSalary" type="java.lang.Double">
<column name="EMP_SALARY" />
</property>
<many-to-one name="department" class="com.bjlemon.hibernate.entity.Department">
<column name="DEPT_ID" />
</many-to-one>
</class>
</hibernate-mapping>
复制代码
多对多映射
project.java
public class Project {
private Integer proId;
private String proName;
private Set<Developer> developers = new HashSet<>();
}
复制代码
developer.java
public class Developer {
private Integer devId;
private String devName;
private Set<Project> projects = new HashSet<>();
}
复制代码
Project.hbm.xml
<?xml version="1.0"?>
<!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.bjlemon.hibernate.entity.Project" table="PROJECT">
<id name="proId" type="java.lang.Integer">
<column name="PRO_ID" />
<generator class="identity" />
</id>
<property name="proName" type="java.lang.String">
<column name="PRO_NAME" />
</property>
<set name="developers" table="DEV_PRO" inverse="false">
<key>
<column name="PRO_ID" />
</key>
<many-to-many class="com.bjlemon.hibernate.entity.Developer" column="DEV_ID"/>
</set>
</class>
</hibernate-mapping>
复制代码
Developer.hbm.xml
<?xml version="1.0"?>
<!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.bjlemon.hibernate.entity.Developer" table="DEVELOPER">
<id name="devId" type="java.lang.Integer">
<column name="DEV_ID" />
<generator class="native" />
</id>
<property name="devName" type="java.lang.String">
<column name="DEV_NAME" />
</property>
<set name="projects" table="DEV_PRO" inverse="true">
<key>
<column name="DEV_ID" />
</key>
<many-to-many class="com.bjlemon.hibernate.entity.Project" column="PRO_ID"/>
</set>
</class>
</hibernate-mapping>
复制代码
持久化对象的三种状态
- 持久化对象的三个状态:看唯一标识OID是否有值,对象是否被Session对象管理
- 瞬时态:也称临时状态,未被持久化,不处于session的缓存中
获得瞬时态的对象:User user = new User()
瞬时态对象转换持久态:save()/saveOrUpdate();
瞬时态对象转换成脱管态:user.setId(1)
获得持久态的对象:get()/load();
持久态转换成瞬时态对象:delete();
持久态对象转成脱管态对象:session的close()/evict()/clear();
- 脱管态:已经被持久化,但不处于session的缓存中
获得托管态对象:User user = new User();user.setId(1);
脱管态对象转换成持久态对象:update();/saveOrUpdate()/lock();
脱管态对象转换成瞬时态对象:user.setId(null);
hibernate的抓取策略
- 抓取策略:使用Hibernate查询一个对象的时候,查询其关联对象.应该如何查询.是Hibernate的一种优化手段
- Hibernate框架的检索策略解决的问题
查询的时机:lazy属性解决查询的时机的问题,需要配置是否采用延迟加载!!
查询的语句形式:fetch属性就可以解决查询语句的形式的问题
立即检索和延迟检索
- 立即检索:当执行到某条语句的时候,就会马上发送SQL.(get方法)
- 延迟检索:当执行到某条语句的时候,不会马上发送SQL.当真正使用对象的属性的时候,才会发送SQL语句(load方法)
延迟检索分成类级别延迟和关联级别的延迟:
1.类级别的延迟:查询对象的本身的时候是否采用延迟,默认情况下是采用延迟。使延迟加载失效的办法:上配置lazy="false" 如果是true,采用延迟加载,如果是false,不采用
2.关联级别的延迟:类中所关联的类对象是否采用延迟。可以在,等标签上设置lazy属性
抓取策略相关的属性和取值
- 主要是:lazy、fetch:在,这些标签上,使用fetch属性和lazy属性就可以完成抓取策略的配置
- fetch作用在set标签上
fetch的取值 控制SQL语句生成的格式
select 默认值,发送查询语句
join 连接查询,发送的是一条迫切左外连接,配置了join.lazy就失效了
subselect 子查询,发送一条子查询查询其关联对象
lazy的取值 查找关联对象的时候是否采用延迟!
true 默认.延迟
false 不延迟
extra 及其懒惰
fetch的取值 控制SQL语句生成的格式
select 默认值,发送查询语句
join 连接查询,发送的是一条迫切左外连接,配置了join.lazy就失效了
lazy的取值 查找关联对象的时候是否采用延迟!
false 不采用延迟加载
proxy 默认值代理,现在是否采用延迟,由另一端的上的lazy确定。 如果这端的class上的lazy=true,proxy的值就是true(延迟加载),
如果class上lazy=false,proxy的值就是false(不采用延迟)
no-proxy
批量抓取:batch-size
- set元素有一个batch-size属性,用来为延迟检索策略或立即检索策略设定批量检索的数量,批量检索能减少SELECT语句的数目,提高延迟检索或立即检索的运行性能。默认值是1
- class标签上也可以配置批量抓取
Hibernate中的事务与并发
事务就是逻辑上的一组操作,组成事务的各个执行单元,操作要么全都成功,要么全都失败
原子性 事务不可分割.
一致性 事务执行的前后数据的完整性保持一致.
隔离性 一个事务执行的过程中,不应该受到其他的事务的干扰.
持久性 事务一旦提交,数据就永久保持到数据库中.
脏读:一个事务读到了另一个事务未提交的数据.
不可重复读:一个事务读到了另一个事务已经提交的update数据,导致多次查询结果不一致.
幻读:一个事务读到了另一个事务已经提交的insert数据,导致多次查询结构不一致.
未提交读:以上的读的问题都有可能发生.
已提交读:避免脏读,但是不可重复读,幻读都有可能发生.
可重复读:避免脏读,不可重复读,但是幻读是有可能发生.
串行化:以上读的情况都可以避免.
- 如果想在Hibernate的框架中来设置隔离级别,需要在hibernate.cfg.xml的配置文件中通过标签来配置
通过:hibernate.connection.isolation = 4 来配置
取值:
1—Read uncommitted isolation
2—Read committed isolation
4—Repeatable read isolation
8—Serializable isolation
Hibernate的一级缓存(Session对象的一级缓存)
- 什么是缓存
其实就是一块内存空间,将数据源中的数据存放到缓存中。再次获取的时候,直接从缓存中获取,可以提升程序的性能!
- Hibernate框架提供了两种缓存
一级缓存:自带的不可卸载的,一级缓存的生命周期与session一致,一级缓存称为session级别的缓存.
二级缓存:默认没有开启,需要手动配置才可以使用的,二级缓存可以在多个session中共享数据,二级缓存称为是sessionFactory级别的缓存.
Session接口中,有一系列的java的集合,这些java集合构成了Session级别的缓存(一级缓存),将对象存入到一级缓存中。
如果session没有结束生命周期,那么对象就在session中存放着
在同一个Session对象中两次查询,可以证明使用了缓存
- Hibernate框架是如何做到数据发生变化时进行同步操作的呢?
利用快照机制来完成的。
Hibernate的二级缓存的概述
- 内存中存放经常操作的数据可以放入缓存中,减少与数据库的交互,提升性能
- Hibernate一级缓存是Session级别的缓存。这一级别的缓存由 hibernate 管理的,一般情况下无需进行干预
- Hibernate二级缓存是SessionFactory级别的缓存,它是属于进程范围的缓存,被多个Session(线程)共享
Hibernate的二级缓存的并发访问策略的问题
- 由于二级缓存被多线程所共享,就必须要有一定的事务策略,二级缓存可以设定以下4种类型的并发访问策略,每一种访问策略对应一个事务隔离级别
只读缓存(read-only)
读/写缓存(read-write)
不严格的读/写缓存(nonstrict-read-write)
事务缓存(transactional)
- 在上面所介绍的隔离级别中,事务型并发访问策略的隔离级别最高,然后依次是读/写型和不严格读写型,只读型的隔离级别最低。事务的隔离级别越高,并发性能就越低
Hibernate的二级缓存存放的数据
很少被修改
不是很重要的数据, 允许出现偶尔的并发问题
经常被修改
财务数据, 绝对不允许出现并发问题
与其他应用数据共享的数据
解释get和load的方法的区别(面试题)
get是立即加载,load延迟加载,真正使用对象的属性的时候才会发送SQL语句。
get方法查询的结果就是对象的本身,load方法查询的结果是对象的代理对象.
get方法查询一个找不到的记录的时候,返回Null,load方法查询一个找不到的对象的时候,返回ObjectNotFoundException.
Hibernate的检索方式(查询的方式)
例如如下这种代码
Customer customer = ...;
Set orders = customer.getOrders();
Order order = ...;
order.getCustomer();
get或者load的方法的方式
session.get(Cusotmer.class,1);
session.createQuery("hql语句");
session.createCriteria(Customer.class);
面向对象的查询的方式
本地的SQL检索
Hibernate框架的SQL查询
基本语法
SQLQuery sqlQuery = session.createSQLQuery("select * from customers");
sqlQuery.addEntity(Customer.class);
List<Customer> list = sqlQuery.list();
for (Customer customer : list) {
System.out.println(customer);
}
复制代码
HQL的检索(使用Hibernate框架,就尽量使用HQL语句查询)
- HQL(Hibernate Query Language) 是面向对象的查询语言,它和SQL查询语言有些相似
- HQL与SQL的关系
HQL查询语句是面向对象的,Hibernate负责解析HQL查询语句,然后根据对象-关系映射文件中的映射信息,把HQL查询语句翻译成相应的SQL语句
HQL查询语句中的主体是域模型中的类及类的属性
SQL查询语句是与关系数据库绑定在一起的,SQL查询语句中的主体是数据库表及表的字段
HQL查询的演示
List<Customer> list = session.createQuery("from Customer").list();
复制代码
List<Customer> list = session.createQuery("select c from Customer c").list();
复制代码
升序(默认也是是升序)
List<Customer> list = session.createQuery("from Customer order by age").list();
复制代码
降序
List<Customer> list = session.createQuery("from Customer order by age desc").list();
复制代码
Hibernate框架提供了分页的方法,咱们可以调用方法来完成分页。推荐大家使用它的方法。框架会根据不同的数据库,完成不同的分页方式。
两个方法如下:
setFirstResult() 从哪条记录开始(默认是0代表第一条记录)
setMaxResults() 每页查询的记录条数(一般都是固定值)
测试代码如下:
List<Order> list = session.createQuery("from Order").setFirstResult(0).setMaxResults(4).list();
复制代码
查询某一个对象,用get()或者load()的方法
检索某一个对象,使用uniqueResult()该方法
Department department=(Department)session.createQuery("from Department d where d.deptId = ?").setParameter(0, 1).uniqueResult();
复制代码
setParameter("?号的位置,默认从0开始","参数的值"); 不用考虑参数的具体类型
按位置绑定参数的条件查询(指定下标值,默认从0开始)
session.createQuery("from Customer where cname like ?").setParameter(0, "张%").list();
session.createQuery("from Customer where cname = ? and age > ?").setParameter(0, "李四").setParameter(1, 25).list();
复制代码
按名称绑定参数的条件查询(HQL语句中的 ? 号换成 :名称 的方式)
session.createQuery("from Customer where cname like :name").setParameter("name", "张%").list();
session.createQuery("from Customer where cname = :name and age > :age").setParameter("name", "李四").setParameter("age", 25).list();
复制代码
HQL的投影查询
- 投影查询就是想查询某一字段的值或者某几个字段的值
- 投影查询的案例
如果只查询一个字段
List<String> list = session.createQuery("select cname from Customer").list();
for (String string : list) {
System.out.println(string);
}
复制代码
如果查询两个字段
List<Object[]> list = session.createQuery("select cname,age from Customer").list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
复制代码
如果查询两个字段,也可以把这两个字段封装到对象中
先在持久化类中提供对应字段的构造方法
List<Customer> list = session.createQuery("select new Customer(cname,age) from Customer").list();
for (Customer customer : list) {
System.out.println(customer);
}
复制代码
聚集函数
- 聚集函数(一般和分组在一起使用的时候,才会有效果)
count() 计数
sum() 求和
avg() 平均值
max() 最大值
min() 最小值
Query query = session.createQuery("select count(*) from Customer c");
Integer count=(Integer)query.uniqueResult();
System.out.println("count "+count);
Query query = session.createQuery("select avg(c.age) from Customer c");
Double avg=(Double)query.uniqueResult();
System.out.println("avg "+avg);
Query query = session.createQuery("select max(c.age),min(c.age) from Customer c");
Object[] maxmin=(Object[])query.uniqueResult();
System.out.println("max "+(Long)maxmin[0]);
System.out.println("min "+(Long)maxmin[1]);
Query query = session.createQuery("select sum(c.age) from Customer c");
Long sum=(Long)query.uniqueResult();
System.out.println("sum "+sum);
复制代码
相关代码
List<Object[]> list=session.createQuery("select c.cname,count(c) from Customer c group by c.cname").list();
System.out.println(list.size());
for(int i=0;i<list.size();i++){
Object [] o = list.get(i);
System.out.print(o[0]+" : "+o[1]);
}
复制代码
QBC检索方式
List<Customer> list = session.createCriteria(Customer.class).list();
for (Customer customer : list) {
System.out.println(customer);
}
复制代码
- 排序查询(要注意需要使用到Hibernate提供的Order类)
需要使用addOrder()的方法来设置参数,参数使用org.hibernate.criterion.Order对象
Criteria criteria = session.createCriteria(Customer.class);
criteria.addOrder(org.hibernate.criterion.Order.asc("age"));
复制代码
Criteria criteria = session.createCriteria(Order.class);
criteria.setFirstResult(0);
criteria.setMaxResults(10);
List list = criteria.list();
条件查询使用Criteria接口的add方法,用来传入条件。
使用Restrictions的添加条件的方法,来添加条件,例如:
Restrictions.eq =
Restrictions.gt >
Restrictions.ge >=
Restrictions.lt <
Restrictions.le <=
Restrictions.between between
Restrictions.like like
Restrictions.in in
Restrictions.and and
Restrictions.or or
Restrictions.asc() asc
Restrictions.desc() desc
Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname", "张%"));
List<Customer> list = criteria.list();
Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.eq("cname", "李四"));
criteria.add(Restrictions.gt("age", 25));
List<Customer> list = criteria.list();
复制代码
多表查询
- 多表的查询进来使用HQL语句进行查询,HQL语句和SQL语句的查询语法比较类似。
- 先回忆多表查询相关的语法
内连接
显示内连接
select * from customers c inner join orders o on c.cid = o.cno;
隐式内连接
select * from customers c,orders o where c.cid = o.cno;
外连接
左外连接
select * from customers c left outer join orders o on c.cid = o.cno;
右外连接
select * from customers c right outer join orders o on c.cid = o.cno;
迫切和非迫切:
非迫切返回结果是Object[]
迫切连接返回的结果是对象,把客户的信息封装到客户的对象中,把订单的信息封装到客户的Set集合中。
说明:HQL语句是支持内连接和外连接的。并且又多提供了两种方式(迫切内连接和迫切左外连接)
内连接使用 inner join ,默认返回的是Object数组
基本的语句:
List<Object[]> list = session.createQuery("from Customer c inner join c.orders").list();
复制代码
问题
默认把查询的数据封装到Object[]的数组中。
如果想把数据封装到Customer对象中,需要这样来编写SQL语句
List<Customer> list = session.createQuery("select c from Customer c inner join c.orders").list();
复制代码
想去掉重复的数据,使用distinct关键字
迫切内连接:inner join fetch ,返回的是实体对象
迫切内连接和普通的内连接的SQL语句都相同
List<Customer> list = session.createQuery("select distinct c from Customer c inner join fetch c.orders").list();
复制代码
外连接:
左外连接: 封装成List<Object[]>
迫切左外连接:封装成List<Customer>
复制代码
List<Customer> list = session.createQuery("select distinct c from Customer c left outer join c.orders").list();
复制代码
或者使用set集合来去掉重复的数据
List<Customer> list = session.createQuery("from Customer c left outer join fetch c.orders").list();
Set<Customer> set = new HashSet<Customer>(list);
for (Customer c : set) {
System.out.println(c);
}
复制代码