1 持久化类及编写规则
2 主键生成策略
3 实体类状态
4 一级缓存
5 快照机制
6 绑定Session到当前线程
7 Hibernate标准查询
8 HQL查询
1 持久化类及编写规则
持久化类就是实体类。
实体类的包名:entity(实体)、pojo(简单的Java对象)、domian(域对象)
编写规则:
- 属性的声明必须使用私有的private
- 通过get,set方法获得设置和获得属性值
- 属性的声明不能使用final关键字
- 建议实现序列接口Serializable
import java.io.Serializable;
public class Customer implements Serializable {
private static final long serialVersionUID = 194674031268504970L;
private Long custId; // 客户编号(主键)
private String custName; // 客户名称(公司名称)
private String custSource; // 客户信息来源
private String custIndustry; // 客户所属行业
private String custLevel; // 客户级别
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
}
2 主键生成策略
主键生成策略就是Hibernate提供了多种生成主键值的方法。
2.1 常用的主键生成策略
策略名 | 说明 |
---|---|
increment | increment策略是指,不使用数据库本地的自增长策略,而是由程序产生一个自增长的ID值,赋予数据库。 |
identity | identity策略,指定使用数据库中的ID自增长策略。只能用于有ID自增长功能的数据库,如:MySQL、SQLServer…不支持没有ID自增长策略的数据库,如:Oracle、DB2… |
sequence | 使用序列的自增长策略,主要用于有序列的数据库,如:Oracle、DB2。如果用于不支持序列的数据库(如:MySQL),该策略会使用一个表模拟序列。 |
native | 使用数据库本地的策略,就是数据库里面使用什么策略就用什么策略。Hibernate不做任何的判断。如:MySQL数据库使用了increment_auto自增长策略,使用native表示直接调用数据库里面的increment_auto策略。 |
uuid | 就是数据库的主键是使用一个唯一的字符串的来存储。这个唯一的字符串就是UUID |
assigned | assigned策略就是不使用主键生成策略,由手工输入ID |
2.2 increment生成策略
increment策略是指,不使用数据库本地的自增长策略,而是由Hibernate框架产生一个自增长的ID值,赋予数据库的主键。
- 好处:兼容号,可以支持各种数据库。
- 缺点:由于主键值由框架生成,所以效率相对低。
- 应用场景:适合一些需要支持多种数据库的产品型项目。
映射文件中的配置如下:
<id name="custId" column="cust_id">
<!-- ID 生成策略 -->
<!-- increment:由框架生成ID自增长列的值 -->
<generator class="increment"></generator>
</id>
2.3 identity之间生成策略
identity策略,指定使用数据库中的ID自增长策略。只能用于有ID自增长功能的数据库(如:MySQL、SQLServer…),不支持没有ID自增长策略的数据库(如:Oracle、DB2…)
- 好处:指定使用数据库中的ID自增长策略,效率高。
映射文件中的配置如下:
<id name="custId" column="cust_id">
<!-- ID 生成策略 -->
<!-- identity:指定使用数据库中的ID自增长策略 -->
<generator class="identity"></generator>
</id>
2.4 native生成策略
使用数据库本地的策略,就是数据库里面使用什么策略就用什么策略。Hibernate不做任何的判断。如:MySQL数据库使用了increment_auto自增长策略,使用native表示直接调用数据库里面的increment_auto策略。
映射文件中的配置如下:
<id name="custId" column="cust_id">
<!-- ID 生成策略 -->
<generator class="native"></generator>
</id>
native生成策略支持所有的关系型数据库。Oracle使用native生成策略,缺点是:序列名固定为:HIBERNATE_SEQUENCE,并且多个表使用一个序列,会导致序列断号。
2.5 sequence生成策略
使用序列的自增长策略,主要用于有序列的数据库,如:Oracle、DB2。如果用于不支持序列的数据库(如:MySQL),该策略会使用一个表模拟序列。
映射文件中的配置如下:
<id name="custId" column="cust_id">
<!-- ID生成策略 -->
<!--
sequence:使用序列策略
序列是需要序列名的,如果不配置默认为HIBERNATE_SEQUENCE
通过参数可以配置序列名、补偿、开始位置
注意:所有策略的字符串都对应着一个类
如:sequence对应的类就是org.hibernate.id.enhanced.SequenceStyleGenerator
所有策略对应的类都可以在hibernate-core包下的org.hibernate.id包下找到
-->
<!-- <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator"></generator> -->
<generator class="sequence">
<!-- 配置参数 -->
<!-- 参数,序列名 -->
<param name="sequence_name">seq_customer</param>
<!-- 参数,开始位置,默认就是1 -->
<param name="initial_value">1</param>
<!-- 参数,步长,默认就是1 -->
<param name="increment_size">1</param>
</generator>
</id>
2.6 uuid主键生成策略
就是数据库的主键是使用一个唯一的字符串的来存储。这个唯一的字符串就是UUID。
实体类代码:
// UUID使用字符串存储主键。
private String custId;
public String getCustId() {
return custId;
}
public void setCustId(String custId) {
this.custId = custId;
}
映射文件中的配置如下:
<id name="custId" column="cust_id">
<!-- ID生成策略 -->
<!-- uuid:使用一个唯一的字符串值作为主键的值 -->
<generator class="uuid"></generator>
</id>
2.7 assigned主键生成策略
assigned策略就是不使用主键生成策略,由手工输入ID。
映射文件中的配置如下:
<id name="custId" column="cust_id">
<!-- ID生成策略 -->
<!-- assigned -->
<generator class="assigned"></generator>
</id>
调用代码,必须要有ID值:
@Test
public void save(){
// 1.获得操作对象
Session session = HibernateUtils.getSession();
// 2.打开事务,操作都需要打开事务(增删改)
Transaction transaction = session.beginTransaction();
// 3.操作
Customer customer = new Customer();
// 配置了assigned主键策略,表示ID值不会自动生成,需要手工输入。
customer.setCustId(1L);
customer.setCustName("阿里");
session.save(customer);
// 4.提交事务
transaction.commit();
// 5.关闭操作对象session
session.close();
} // 如果调用代码没有ID值,会报异常
3 实体类状态转换
实体类(持久化类)是由状态的。
Hibernate是一个先映射,后操作的框架。所谓的状态就是实体类的对象和数据库是否有关联的情况。
3.1 瞬时态
使用new创建一个对象,这时实体对象和数据库没有任何关系。如:
Customer customer = new Customer();
此时,对象的状态为瞬时态(自由态)。
3.2 持久态
创建的对象被session操作过了。如:
@Test
public void save(){
// 1.获得操作对象
Session session = HibernateUtils.getSession();
// 2.打开事务,操作都需要打开事务(增删改)
Transaction transaction = session.beginTransaction();
// 3.操作
Customer customer = new Customer();
customer.setCustName("百度");
session.save(customer);
// 这个时候customer,被session操作过了,和数据库建立了关系。
// 4.提交事务
transaction.commit();
// 5.关闭操作类对象session
session.close();
}
将被session操作过的对象的状态称为持久态(托管态)。
3.3 游离态
创建的对象被session操作后,session关闭了。如:
@Test
public void get(){
// 1.获得操作对象
Session session = HibernateUtils.getSession();
// 通过OID获得对应的记录
Customer customer = session.get(Customer.class, 2L);
session.close();
// 虽然customer是通过session获得的,但session关闭了,此时为游离态
System.out.println(customer.getCustName());
}
被session操作过后,session失效了。此时实体对象的状态为游离态。
3.4 状态转移图
重点:要知道如何获得持久态的对象。
4 一级缓存
Hibernate是支持一级缓存的。所谓的一级缓存就是Session级别的缓存。就是说,同一个session执行多次同样的查询(get/load),只会查询一次,之后直接返回缓存的数据。
4.1 Hibernate一级缓存示例
@Test
public void get() {
// 1.获得操作对象
Session session = HibernateUtils.getSession();
// get方法通过OID获得对应的记录
// 如果支持缓存,get四次数据库,只查一次。
Customer customer01 = session.get(Customer.class, 1L);
System.out.println(customer01.getCustName());
Customer customer02 = session.get(Customer.class, 1L);
System.out.println(customer02.getCustName());
Customer customer03 = session.get(Customer.class, 1L);
System.out.println(customer03.getCustName());
Customer customer04 = session.get(Customer.class, 1L);
System.out.println(customer04.getCustName());
session.close();
} // 四个get方法,只会有一条SQL语句!
4.2 Hibernate清空缓存的方法
public void get(){
// 1.获得操作对象
Session session = HibernateUtils.getSession();
// get方法通过OID获得对应的记录
// 如果支持缓存,get四次数据库,只查一次。
Customer customer1 = session.get(Customer.class, 1L);
System.out.println(customer1.getCustName());
// clear()清空同一个session的所有持久化对象。缓存被清空
session.clear();
Customer customer2 = session.get(Customer.class, 1L);
System.out.println(customer2.getCustName());
// evict()清空指定的持久化对象,该对象的缓存被清空
session.evict(customer2);
Customer customer3 = session.get(Customer.class, 1L);
System.out.println(customer3.getCustName());
Customer customer4 = session.get(Customer.class, 1L);
System.out.println(customer4.getCustName());
// 表示关闭了session。session的所有缓存被清空
session.close();
} // 会产生3条SQL语句
5 快照机制
当对象变成持久态对象,和数据库表关联后,在session中会保存两份数据的副本:一份是缓存,一个是快照。
- 缓存的作用:用于提高查询的效率。
- 快照的作用:用于更新数据,作对比。
持久态对象支持快照,直接可以通过修改持久化对象的属性更新数据。
@Test
public void update(){
// 1.获得操作对象
Session session = HibernateUtils.getSession();
// 2.开启事务
Transaction transaction = session.beginTransaction();
// 3.获得一个持久态对象。
Customer customer = session.get(Customer.class,1L);
// 只要修改了字段,提交事务就可以直接更新到数据库
customer.setCustSource("互联网");
// 4.提交事务
transaction.commit();
// 5.关闭会话对象
session.close();
System.out.println(customer.getCustName());
}
实现原理图:
6 绑定Session到当前线程
绑定Session到当前线程,就是在同一条线程中的session是相同的。
配置文件(hibernate.cfg.xml)绑定线程的配置:
<!-- 绑定线程 -->
<property name="hibernate.current_session_context_class">thread</property>
修改SessionFactory获得Session的代码:
public class HibernateUtils {
// 如果一个程序中有多个连接池,有可能会导致事物处理无法提交,所以要保证程序里只有一个连接池
public static SessionFactory sessionFactory = HibernateUtils.createSessionFactory();
// 1.获得一个会话工厂
private static SessionFactory createSessionFactory() {
// (1)创建一个Configuration的对象
Configuration config = new Configuration();
// (2)读取默认路径下的hibernate.cfg.xml配置文件
config.configure();
// (3)构造一个会话工厂
SessionFactory sessionFactory = config.buildSessionFactory();
return sessionFactory;
}
// 2.获得Session
public static Session getSession() {
// return sessionFactory.openSession();
return sessionFactory.getCurrentSession(); // 配置线程绑定后一定要修改获取session方式
}
}
public class CustomerDAO {
public void sava(Customer customer) {
// 获得操作对象
Session session = HibernateUtils.getSession();
// 添加数据
session.save(customer);
}
}
public class CustomerDAOTest {
@Test
public void save() {
// 1.获得操作对象
Session session = HibernateUtils.getSession();
// 2.打开事务。
Transaction transaction = session.beginTransaction();
// 3.操作
Customer customer = new Customer();
customer.setCustName("阿里");
// 将记录保存到数据库中
CustomerDAO dao = new CustomerDAO();
dao.sava(customer);
// 4.提交事务
transaction.commit();
// 5.如果session绑定到当前线程,session会随线程的启动而启动,会随线程的结束而结束,不能手动关闭。
// session.close();
}
@Test
public void get() {
Session session = HibernateUtils.getSession();
// 如果使用了线程绑定,操作必须要开启事务,包括查询
session.beginTransaction();
Customer customer = session.get(Customer.class, 1L);
System.out.println(customer.getCustName());
}
}
7 Hibernate查询对象API
7.1 标准查询
标准查询(QBC:Query By Criteria),就是完全通过Java代码查询数据库。
7.2 查询所有数据
/**
* 查询所有的数据(QBC)
*/
@Test
public void findAll() {
// 获得session
Session session = HibernateUtils.getSession();
// 获得Criteria接口,必须指定查询的实体类
Criteria criteria = session.createCriteria(Customer.class);
// 查询所有的数据,返回一个实体类的集合
List<Customer> customers = criteria.list();
// 遍历集合
for(Customer customer : customers) {
System.out.println(customer.getCustName());
}
// 关闭session
session.close();
}
7.3 条件查询
QBC限制条件的说明:
短语 | 含义 |
---|---|
Restrictions.eq | 等于= |
Restrictions.allEq | 使用Map,使用key/value进行多个等于的判断 |
Restrictions.gt | 大于> |
Restrictions.ge | 大于等于>= |
Restrictions.lt | 小于< |
Restrictions.le | 小于等于<= |
Restrictions.between | 对应sql的between子句 |
Restrictions.like | 对应sql的like子句 |
Restrictions.in | 对应sql的in子句 |
Restrictions.and | and关系 |
Restrictions.or | or关系 |
Restrictions.sqlRestriction | sql限定查询 |
Restrictions.asc() | 根据传入的字段进行升序排序 |
Restrictions.desc() | 根据传入的字段进行降序排序 |
/**
* 条件查询,通过客户名模糊查询
*/
@Test
public void findByName() {
// 获得session
Session session = HibernateUtils.getSession();
// 获得Criteria接口,必须指定查询的实体类
Criteria criteria = session.createCriteria(Customer.class);
// 设置查询的条件
criteria.add(Restrictions.like("custName", "%百%"));
// 查询所有的数据,返回一个实体类的集合
List<Customer> customers = criteria.list();
// 遍历集合
for(Customer customer : customers) {
System.out.println(customer.getCustName());
}
// 关闭session
session.close();
}
7.4 分页查询
/**
* 分页查询
*/
@Test
public void findByPage() {
// 获得session
Session session = HibernateUtils.getSession();
// 获得Criteria接口,必须指定查询的实体类
Criteria criteria = session.createCriteria(Customer.class);
// 设置分页开始的位置。编号是从0开始的。要求取出表中第三条到第五条的数据。
criteria.setFirstResult(2);
// 设置取出多少条数据。第三条到第五条总共3条数据
criteria.setMaxResults(3);
// 查询所有的数据,返回一个实体类的集合
List<Customer> customers = criteria.list();
// 遍历集合
for(Customer customer : customers) {
System.out.println(customer.getCustName());
}
// 关闭session
session.close();
}
8 HQL查询
8.1 什么是HQL
HQL(Hibernate Query Language):Hibernate查询语言。就是一套类似SQL的语言,但是HQL操作的是持久化对象。
8.2 获得所有数据
/**
* 通过HQL查询所有数据
*/
@Test
public void findAll() {
// 获得操作对象
Session session = HibernateUtils.getSession();
// 获得一个查询HQL的接口Query
Query query = session.createQuery("from Customer");
// 通过list方法获得所有数据
List<Customer> customers = query.list();
// 遍历
for(Customer customer : customers) {
System.out.println(customer.getCustName());
}
// 关闭会话对象
session.close();
}
/**
* 通过HQL查询所有数据(使用select)
*/
@Test
public void findAll02() {
// 获得操作对象
Session session = HibernateUtils.getSession();
// 获得一个查询HQL的接口Query
// 注意:如果需要使用select返回数据,返回的是一个对象,不能使用*
Query query = session.createQuery("select c from Customer c");
// 通过list方法获得所有数据
List<Customer> customers = query.list();
// 遍历
for(Customer customer : customers) {
System.out.println(customer.getCustName());
}
// 关闭会话对象
session.close();
}
8.3 条件查询
/**
* 通过HQL查询名字带“百”的客户(问号?)
*/
@Test
public void findByName() {
// 获得操作对象
Session session = HibernateUtils.getSession();
// 获得一个查询HQL的接口Query
Query query = session.createQuery("from Customer c where c.custName like ?");
// 设置参数,注意:Hibernate的索引是从0开始的
query.setString(0, "%百%");
// 通过list方法获得所有数据
List<Customer> customers = query.list();
// 遍历
for(Customer customer : customers) {
System.out.println(customer.getCustName());
}
// 关闭会话对象
session.close();
}
/**
* 通过HQL查询名字带“百”的客户(命名参数)
*/
@Test
public void findByName02() {
// 获得操作对象
Session session = HibernateUtils.getSession();
// 获得一个查询HQL的接口Query
Query query = session.createQuery("from Customer c where c.custName like :cname");
// 设置参数,注意:Hibernate的索引是从0开始的
query.setString("cname", "%百%");
// 通过list方法获得所有数据
List<Customer> customers = query.list();
// 遍历
for(Customer customer : customers) {
System.out.println(customer.getCustName());
}
// 关闭会话对象
session.close();
}
8.4 统计记录数据
/**
* 通过HQL统计所有数据
*/
@Test
public void count() {
// 获得操作对象
Session session = HibernateUtils.getSession();
// 获得一个查询HQL的接口Query
// 注意:count(*)是统计记录数的固定写法,返回Long类型
Query query = session.createQuery("select count(*) from Customer");
// 如果返回的只有一个值,使用uniqueResult方法
Long count = (Long) query.uniqueResult();
System.out.println(count);
// 关闭会话对象
session.close();
}
8.5 分页查询
/**
* 通过HQL分页查询数据
*/
@Test
public void findByPage() {
// 获得操作对象
Session session = HibernateUtils.getSession();
// 获得一个查询HQL的接口Query
Query query = session.createQuery("from Customer");
// 要在查询之前分页。索引从0开始,所以第三条数据为2
query.setFirstResult(2);
// 查询3条数据
query.setMaxResults(3);
// 通过list方法获得分页的数据
List<Customer> customers = query.list();
// 遍历
for(Customer customer : customers) {
System.out.println(customer.getCustName());
}
// 关闭会话对象
session.close();
}
8.6 投影查询
当查询的记录不是所有字段,而是指定的字段, 如果需要使用一个实体类接收,就需要一个有参数的构造方法。我们将这种有参数的查询,称为投影查询。
/**
* 通过HQL查询客户表,只需要查出custName,custSource
*/
@Test
public void findCustomer() {
// 获得操作对象
Session session = HibernateUtils.getSession();
// 获得一个查询HQL的接口Query
Query query = session.createQuery("select custName,custSource from Customer");
// 通过list方法获得所有数据。返回的是一个Object[]数组的集合。数组中包含两个元素:custName和custSource的值。这种方式很不方便,不推荐!
List<Object[]> customers = query.list();
// 遍历
for(Object[] customer : customers) {
System.out.println(customer[0]);
System.out.println(customer[1]);
}
// 关闭会话对象
session.close();
}
/**
* 通过HQL查询客户表,只需要查出custName,custSource(通过有参数的构造函数实现)
* 实体类中必须要有相应的有参数的构造函数
*/
@Test
public void findCustomer02() {
// 获得操作对象
Session session = HibernateUtils.getSession();
// 获得一个查询HQL的接口Query
Query query = session.createQuery("select new Customer(custName,custSource) from Customer");
// 通过list方法获得所有数据。
List<Customer> customers = query.list();
// 遍历
for(Customer customer : customers) {
System.out.println(customer.getCustName());
System.out.println(customer.getCustSource());
}
// 关闭会话对象
session.close();
}