Hibernate QBC查询
Hibernate day_02
- 持久化类以及编写规则.
持久化类就是我们说的实体类(符合 JavaBean 的规则)。
以后分包:entity(实体),pojo(简单的 Java 对象),domian(域对象)
1. 属性的声明必须使用私有的 private
2. 通过 get,set 方法获得设置和获得属性值
3. 属性的声明不能使用 final 关键字
4. [可选]建议实现一个序列接口 Serializable.如果需要将对象缓存到本地文件,必须加上
- 主键生成策略
所谓的主键生成策略,就是 Hibernate 提供了多种生成主键值的方法
常用的策略有:increment identity sequence native uuid assigned
-
- increment策略
不使用数据库的本地的增长策略,而是由程序(Hibernate框架)产生一个自增长的ID 值,赋予数据库.
<generator class="increment"></generator>
这种策略的好处:
由于ID自增长的值由程序生成,忽略了各种数据库ID自增长的差异,所以兼容性好
坏处:
因为每次增加一个ID值,都需要调用max()函数,效率不高。
应用场景:
- 是用在一些需要支持多种数据库的产品型项目。!!
- identity策略(只能用于有 ID 自增长类型的数据库 如 mysql sqlserver ,对不支持自增长策略的数据库 如 oracle db2)在配置前 一定要设置相应表格的 id 自增长
这种策略的好处:效率高。因为直接认为数据库就是使用ID自增长的!!
缺点:兼容性差,只支持有ID自增长的数据库。
-
- native 策略
根据数据库的本地策略生成主键值,如果数据库支持ID自增长策略的,使用ID自增长。如果数据库使用的是序列来生成ID值的,那么就是序列!!
--native效率高于increment,低于identity和sequence.由于不上不下的效率,使用不多!
-
- uuid 策略(注意 对应的字段必须为字符串)
所谓的uuid就是一个唯一的字符串。一般是32位。
那么什么时候使用UUID呢?
应用场景:
有几个开发人员同时开发一个项目。前提每个开发人员使用的都是自己电脑的数据库。
如果使用 ID 自增长作为 ID 列的值,就会导致每个人的数据的 ID 冲突。
但是如果大家使用 UUID。那么冲突的概率,极小
UUID 用于存储基础数据的表。所谓的基础数据,就是系统必须依赖的数据。
-
- assigned策略
HIbernate不使用任何的数据库策略,由调用方手工输入。
手工输入使用的概率不多。一般使用手工输入ID的策略用于一对一的情况
-
- sequence
序列策略:一般用于有序列的数据库。如果像MySQL数据库这种没有序列的数据库使用sequence策略,会使用的一个表来模拟序列。ID值生成规则存放在这个模拟表里面!
- 实体类状态转换(难点) 会获得持久态对象
实体类(持久化类)对象是有状态的。
为什么实体类对象会有状态?
答:由于 HIbernate 框架是一个先映射,后操作的框架。所谓的状态就是实体类的对象和数据库是否
有关联的情况。
男孩比作一个对象
女孩子比作一个数据库对象
Hibernate 的持久化类有三种状态:
1. 瞬时态(自由态):与数据库的表没有任何关联关系的实体对象
Customer customer=new Customer();
2. 持久态:正在与数据库保持连接的关系。
Customer customer=session.get(Customer.class,2L);
3. 游离态:曾经被 session 操作过,但 session 失效了。关闭,清除
-
- 瞬时态:
Customer c=new Customer();
-
- 持久态:
- 创建的对象被 sesssion 操作过了 ,操作不包括删除!!
@Test public void save(){ //1.获得数据库的操作对象,session Session session = HibernateUtils.getSession(); //2.Hibernate默认必须先启动事务,才可以操作(增删改) Transaction transaction = session.beginTransaction(); //3.封装一个有数据的实体类对象 Customer c=new Customer(); c.setCustName("阿里巴巴"); //4.保存 session.save(c); //5.提交 transaction.commit(); //这个时候 c,被 session 操作过了。就和数据库建立关系。 //6.关闭 session.close(); } |
-
- 游离态: 创建的对象被 sesssion 操作过了,session 关闭了。
瞬时态和游离态,这个两个状态都是和数据库没有关联了。他们区别是:是否曾经被操作过!!
对象状态转移图如下:
注意:根据状态转移图,我们学会如何获得持久态对象就可以(会使用get方法就可以了)了!!!
为什么需要了解如何获得持久态对象呢?
答:因为持久态对象是正在和数据库关联的状态的对象。所以支持以下数据库的操作。
- 快照机制支持
- 缓存机制的支持
- 导航查询的支持。
- 一级缓存
HIbernate 的持久态对象是支持一级缓存。所谓的一级缓存就是 Session 级别的缓存。
意思就是说,同一个 session 查询同样的数据,只查询一次数据库。如果出现同多次同样的查询(get/load)
直接返回缓存的数据
问题:如何清除一级缓存?
答:关闭session。和清空session。
. session.close() session.clear()
注意,close,clear,evit 清空缓存只是将持久态转成游离态,清空的是数据和数据库的关联,而不是清空数据。对象的属性和数据依然存在.
清除缓存的应用场景:秒杀,团购。遇到数据的数据不断更新,而查询的session又不能不断关闭。所以每次查询数据库表之前,需要清空清除一个缓存!!
- 快照机制
当对象变成持久态对象(调用get方法时候)的时候,和数据库表关联后。在 session 中会保存两份数据的副本
一份是缓存,一个是快照。
缓存的作用:用于提高查询的效率
快照的作用:用于更新数据,作对比使用。
快照的支持就是持久态对象直接可以通过修改属性更新数据,不需要 update 方法
这里修改持久态对象的属性 直接对该对象数据库中的信息进行更新.
@Test public void update() { // 1.获得数据库的操作对象,session Session session = HibernateUtils.getSession(); //2.更新需要启动事务 Transaction transaction = session.beginTransaction(); //获得持久态对象 Customer customer = session.get(Customer.class, 1L); //修改属性 customer.setCustName("alibaba"); //提交 transaction.commit();
// .关闭 session.close();
} |
执行流程图:
- 线程绑定(比进程更小的运行单位) web项目理解为多线程 每一次请求都是一个线程(Session绑定)
所谓的线程绑定,就是将session的对象绑定到当前的线程变量里面。这样确保了在同一条线程中使用的session对象是相同的!!!
为什么需要线程绑定呢?
答:如果不使用线程绑定,要处理同时对数据库两个操作的业务,需要通过参数传递的方式来确保session的唯一的。
为什么同时操作两个业务的需求,需要session唯一呢?
答:因为数据库事务处理的前提,必须是同一个连接(同一个session)
实现线程绑定的方式:
方式一:
-
- 使用HIbernate的内置实现
Hibernate框架内置支持将对象绑定到当前线程
<property name="hibernate.current_session_context_class">thread</property>
这个时候获得 session 的时候不能够使用 openSession() 方法 要使用 getCurrentSession()方法 获得当前Session 对象.
注意标注红色字体部分.
public class HibernateUtils { //通过一个静态变量,确保整个项目里面只有一个SessionFactory对象 //为什么建议一个项目只有一个SessionFactory对象? //答:如果一个项目有多个连接池,可以导致事务不同步!!! public static SessionFactory sessionFactory=HibernateUtils.createSessionFactory();
//获得会话工厂 private static SessionFactory createSessionFactory(){ //1.获得Configuration对象 Configuration config=new Configuration(); //2.读取配置文件,默认读取的就是classpath根目录的hibernate.cfg.xml config.configure(); //3.构建会话工厂 SessionFactory sessionFactory = config.buildSessionFactory(); return sessionFactory; }
//获得会话,就是一个操作对象 public static Session getSession(){ //1.每次都需要在当前线程获得session对象 //注意:getCurrentSession必须要先配置线程绑定才可以使用 return sessionFactory.getCurrentSession(); }
//测试 public static void main(String[] args) { System.out.println(HibernateUtils.getSession()); }
} |
注意事项:
//注意:hibernate内置实现的线程绑定,已经实现随线程启动而启动,随线程关闭而关闭,所以session不能手工关闭
//session.close();
//注意:实现了内置线程绑定后,必须要先启动事务,才可以查询。所以增删改查都需要开启事务!!!
-
- 第二:使用自定义的方式实现 线程绑定.实现代码:
public class HibernateUtils { //通过一个静态变量,确保整个项目里面只有一个SessionFactory对象 //为什么建议一个项目只有一个SessionFactory对象? //答:如果一个项目有多个连接池,可以导致事务不同步!!! public static SessionFactory sessionFactory=HibernateUtils.createSessionFactory(); //声明一个线程变量,作用就是确保存在在线程变量里面的对象,同一条线程是相同的。 private static ThreadLocal<Session> threadLocal=new ThreadLocal<>();
//获得会话工厂 private static SessionFactory createSessionFactory(){ //1.获得Configuration对象 Configuration config=new Configuration(); //2.读取配置文件,默认读取的就是classpath根目录的hibernate.cfg.xml config.configure(); //3.构建会话工厂 SessionFactory sessionFactory = config.buildSessionFactory(); return sessionFactory; }
//获得会话,就是一个操作对象 public static Session getSession(){ //判断,线程变量是否存在session if(threadLocal.get()==null){ Session session = sessionFactory.openSession(); //将session对象放在线程变量里面 threadLocal.set(session); } return threadLocal.get(); }
//实现一个关闭的方法,在关闭session后,要移除线程变量里面的session对象 public static void closeSession(){ if(threadLocal.get()!=null){ Session session = threadLocal.get(); //关闭只是session和数据库断开了,但对象还在!! session.close(); //关闭session后,必须要将线程变量里面的session移除 threadLocal.remove(); } }
|
- Hibernate查询 API: get(类字节码,id)
- QBC查询:Query By Criteria。(通过Criteria查询API查询)
- 示例代码(查询所有客户):
- QBC查询:Query By Criteria。(通过Criteria查询API查询)
//需求:查询所有的客户,使用Criteria实现 @Test public void findAll(){ //1.获得操作对象,session Session session = HibernateUtils.getSession(); //2.获得Criteria查询对象 Criteria criteria = session.createCriteria(Customer.class); //3.通过criteria对象,查询数据,返回多条数据,使用list List<Customer> customers = criteria.list(); for(Customer c:customers){ System.out.println(c.getCustName()); } session.close(); } |
-
-
- 模糊查询:
-
/** * 需求:查询客户名有"百"的客户 */ @Test public void findByName(){ //1.获得操作对象,session Session session = HibernateUtils.getSession(); //2.获得Criteria查询对象 Criteria criteria = session.createCriteria(Customer.class); //设置条件 criteria.add(Restrictions.like("custName", "%百%")); //3.通过criteria对象,查询数据,返回多条数据,使用list List<Customer> customers = criteria.list(); for(Customer c:customers){ System.out.println(c.getCustName()); } session.close(); }
|
-
-
- 分页查询:
-
//需求:查询第 3 条开始,取 4 条件数据 @Test public void findByPage(){ //1.获得操作对象,session Session session = HibernateUtils.getSession(); //2.获得Criteria查询对象 Criteria criteria = session.createCriteria(Customer.class); //设置分页 //(1)设置开始的位置,开始位置从0开始,第三条数据的下标为2 criteria.setFirstResult(2); //(2)设置每页的记录数,每页返回的数据时4条,设置为4 criteria.setMaxResults(4); //3.通过criteria对象,查询数据,返回多条数据,使用list List<Customer> customers = criteria.list(); for(Customer c:customers){ System.out.println(c.getCustName());
|
-
-
HQL查询(实际开发中较常用)
- Hql(hibernate query language)查询所有客户:
-
HQL查询(实际开发中较常用)
package com.bdqn.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.junit.Test; import com.bdqn.entity.Customer; import com.bdqn.utils.HibernateUtils; public class CustomerDAOTest { // 需求:查询所有的客户,使用hql实现 @Test public void findAll() { // 1.获得操作对象,session Session session = HibernateUtils.getSession(); // 2.获得hql查询对象 Query query = session.createQuery("from Customer");
// 3.通过criteria对象,查询数据,返回多条数据,使用list List<Customer> customers = query.list(); for (Customer c : customers) { System.out.println(c.getCustName()); } session.close(); }
@Test public void findAll1() { // 1.获得操作对象,session Session session = HibernateUtils.getSession(); // 2.获得hql查询对象 // 注意,select返回不能是*,必须是一个属性或者对象的别名 Query query = session.createQuery("select c from Customer c");
// 3.通过criteria对象,查询数据,返回多条数据,使用list List<Customer> customers = query.list(); for (Customer c : customers) { System.out.println(c.getCustName()); } session.close(); }
|
-
-
- Hql模糊查询
-
/** * 需求:查询客户名有"百"的客户 */ @Test public void findByName() { // 1.获得操作对象,session Session session = HibernateUtils.getSession(); // 2.获得Criteria查询对象 Query query = session.createQuery("from Customer c where c.custName like ?"); // 设置条件,注意,设置的下标0,为hql的第一个?的值 query.setString(0, "%百%"); // 3.通过criteria对象,查询数据,返回多条数据,使用list List<Customer> customers = query.list(); for (Customer c : customers) { System.out.println(c.getCustName()); } session.close(); }
|
-
-
- Hql分页查询:
-
// 需求:查询第 3 条开始,取 4 条件数据 @Test public void findByPage() { // 1.获得操作对象,session Session session = HibernateUtils.getSession(); // 2.获得hql查询对象 Query query = session.createQuery("from Customer"); // 设置分页条件 // (1)设置开始位置,从0开始,第三条数据下标为2 query.setFirstResult(2); // (2)设置每页的记录数据,为4 query.setMaxResults(4);
// 3.通过criteria对象,查询数据,返回多条数据,使用list List<Customer> customers = query.list(); for (Customer c : customers) { System.out.println(c.getCustName()); } session.close(); }
|
-
-
- Hql查询总记录:
-
// 需求:统计记录数据 @Test public void count() { // 1.获得操作对象,session Session session = HibernateUtils.getSession(); // 2.获得hql查询对象 // 注意,select返回不能是*,必须是一个属性或者对象的别名 Query query = session.createQuery("select count(c) from Customer c"); // 3.返回一条数据,使用uniqueResult // 注意:如果返回的数据不确定,随便设置一个类型让它报错。通过错误信息分析返回的类型 Long uniqueResult = (Long) query.uniqueResult(); System.out.println(uniqueResult); session.close(); }
|
-
-
- Hql:投影查询(查询部分属性) 了解
-
// 需求:查询客户的信息,返回custName和custSource @SuppressWarnings("unchecked") @Test public void findAll2() { // 1.获得操作对象,session Session session = HibernateUtils.getSession(); // 2.获得hql查询对象 // 注意,select返回不能是*,必须是一个属性或者对象的别名 Query query = session.createQuery("select c.custName,c.custSource from Customer c");
// 3.通过criteria对象,查询数据,返回多条数据,使用list List<Object[]> customers = query.list(); for (Object[] object : customers) { System.out.println("客户名:" + object[0] + ",客户来源:" + object[1]); } session.close(); }
// 需求:查询客户的信息,返回custName和custSource,但是必须使用一个customer对象接收 /** * * 当查询的记录不是所有字段。而是指定的字段。 * 如果需要使用一个实体类接收。那么需要一个有参数的构造方法。我们将这种,有构造方法参数的查询,称为投影查询 */ @Test public void findAll3() { // 1.获得操作对象,session Session session = HibernateUtils.getSession(); // 2.获得hql查询对象 // 注意,select返回不能是*,必须是一个属性或者对象的别名 Query query = session.createQuery("select new Customer(c.custName,c.custSource) from Customer c");
// 3.通过criteria对象,查询数据,返回多条数据,使用list List<Customer> customers = query.list(); for (Customer c : customers) { System.out.println("客户名:" + c.getCustName() + ",客户来源:" + c.getCustSource()); } session.close(); }
}
|
-
- HQL操作
所谓的HQL操作,就是使用HQL实现数据库的删改。
-
-
- Hql删除:
-
注意:HQL是不支持增加的!!!
public class CustomerDAOTest {
//需求:删除客户名有"百"的客户 @Test public void delete(){ //1.获得操作对象 Session session = HibernateUtils.getSession(); //2.操作需要开启事务 Transaction transaction = session.beginTransaction(); //3.获得hql操作对象 Query query = session.createQuery("delete from Customer c where c.custName like ?"); //4.设置删除的条件 query.setString(0, "%百%");
//5.执行hql,返回的是影响行数 int count = query.executeUpdate(); System.out.println(count); transaction.commit(); session.close(); }
//需求:删除客户名有"百"的客户,使用命名参数实现 //所谓的命名参数,就是使用一个自定义的名字代替原来的? @Test public void delete1(){ //1.获得操作对象 Session session = HibernateUtils.getSession(); //2.操作需要开启事务 Transaction transaction = session.beginTransaction(); //3.获得hql操作对象 //注意:命名参数声明的时候,使用有 :(冒号的) Query query = session.createQuery("delete from Customer c where c.custName like :custName"); //4.设置删除的条件,设置条件的时候,命名参数是没有冒号的 query.setString("custName", "%小%");
//5.执行hql,返回的是影响行数 int count = query.executeUpdate(); System.out.println(count); transaction.commit(); session.close(); }
} |
-
-
- Hql更新:
-
//需求:通过hql实现,更新客户名名,有“百”的客户来源,为互联网 @Test public void update(){ //1.获得操作对象 Session session = HibernateUtils.getSession(); //2.操作需要开启事务 Transaction transaction = session.beginTransaction(); //3.获得hql操作对象 //注意:命名参数声明的时候,使用有 :(冒号的) Query query = session.createQuery("update Customer c set c.custSource = ? where c.custName like ?"); //4.设置删除的条件,设置条件的时候,命名参数是没有冒号的 query.setString(0, "互联网"); query.setString(1, "%百%");
//5.执行hql,返回的是影响行数 int count = query.executeUpdate(); System.out.println(count); transaction.commit(); session.close(); }
|
相对于SQL,使用HQL的好处是什么?
答:HQL操作的是对象,不是数据库的表。所以所有的数据库的语法是一样的。屏蔽了不同数据库的方言的差异!!!!
由于Criteria查找接口比较笨重,所以建议使用HQL。
- 总结
1今天学习了各种HIbernate的组件。
ID生成策略
- identity
必须要知道如何获得持久态对象。
- 通过查询可以获得 get(类字节码,id)
- 可以通过更新获得 update(object)
- 可以通过插入获得 save(object)
持久态对象有什么用
- 支持缓存
- 支持快照
- 支持导航查询
线程绑定:目的是为了不用传递参数,在同一条线程操作上,任何位置获得的session是相同的。
为什么有这个需求
原因因为事务处理必须是同一个session才可以实现!!!!!
三种实现线程绑定的方式,会内置配置方式和自定义配置方式。
查找的API。
使用Criteria查找接口。就是使用纯Java对象。查询数据库。(了解)
使用HQL查询接口,通过HQL实现数据库的操作(重点)
相对于SQL,使用HQL的好处是什么?
答:HQL操作的是对象,不是数据库的表。所以所有的数据库的语法是一样的。屏蔽了不同数据库的方言的差异!!!!