Hibernate 开发笔记

流程:

首先加载配置文件,将配置文件传递到properties中,然后创建sessionFactory工厂,在该工厂中创建和数据库的连接,最后拿到其中一个连接session,然后进行CURD。

 

Hibernate执行原理总结

可从度娘上摘抄到如下文字:

  1. 通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。
  2. 由hibernate.cfg.xml中的<mapping resource="com/xx/Xxx.hbm.xml"/>读取解析映射信息。
  3. 通过config.buildSessionFactory();得到sessionFactory。
  4. sessionFactory.openSession();得到session。
  5. session.beginTransaction();开启事务。
  6. persistent operate; 执行你自己的操作。
  7. session.getTransaction().commit();提交事务。
  8. 关闭session。
  9. 关闭sessionFactory。

 

1.导jar包(lib/required)和数据库驱动

 

2.创建实体(持久化类)

cn.itcast.domain包下:

     注意该类的属性名称和类型和数据库相应表对应。

   实现相应get/set方法

3.创建映射文件(持久化类和数据库表字段,并没有配置数据库信息,在核心配置文件中配置)

一个类对应一个,最终要被加载到核心配置文件中。

<hibernate-mapping>

<!-- 建立类与表的映射 -->
<class name="com.itheima.hibernate.demo1.Customer" table="cst_customer">
<!-- 建立类中的属性与表中的主键对应 -->
<id name="cust_id" column="cust_id">
<generator class="native"/>
</id>
<!-- 建立类中的普通的属性和表的字段的对应 -->
<property name="cust_name" column="cust_name"/>
<property name="cust_source" column="cust_source" length="32"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
</class>

</hibernate-mapping>

4.创建Hibernate的核心配置文件(默认是hibernate.cfg.xml)

1.加载数据库相关信息

2.Hibernate的相关配置

3.加载映射配置文件

<hibernate-configuration>
<session-factory>
<!-- 连接数据库的基本参数 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day01?character=utf-8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
<!-- 配置Hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<!-- 可选配置================ -->
<!-- 打印SQL -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化SQL -->
<property name="hibernate.format_sql">true</property>
<!-- 自动创建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>


<!-- Hibernate加载映射-->
<mapping resource="com/itheima/hibernate/demo1/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>

5.测试案列:

// 保存客户的案例
public void demo1(){
// 1.加载Hibernate的核心配置文件
Configuration configuration = new Configuration().configure();
// 手动加载映射
// configuration.addResource("com/itheima/hibernate/demo1/Customer.hbm.xml");
// 2.创建一个SessionFactory对象:类似于JDBC中连接池
SessionFactory sessionFactory = configuration.buildSessionFactory();
// 3.通过SessionFactory获取到Session对象:类似于JDBC中Connection
Session session = sessionFactory.openSession();
// 4.手动开启事务:
Transaction transaction = session.beginTransaction();
// 5.编写代码

Customer customer = new Customer();
customer.setCust_name("王西");

session.save(customer);

// 6.事务提交
transaction.commit();
// 7.资源释放
session.close();
sessionFactory.close();
}

 

6.抽取HiberbateUtils工具类

public class HibernateUtils {


public static final Configuration cfg;
public static final SessionFactory sf;

static{
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}

public static Session openSession(){
return sf.openSession();
}

}

 

7.Hibernate的API

7.1 Configuration:Hibernate的配置对象

作用: 加载核心配置文件(默认加载hibernate.cfg.xml)

Configuration cfg = new Configuration().configure();

 

7.2 SessionFactory:Session工厂(对应了核心配置文件的<session-factory> XXXXX </session-factory>标签,在加载完核心配置文件后,得到了properties里的各属性(大多数是和数据库的映射),方便创建该工厂类)。

需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够

SessionFactory内部维护了Hibernate的连接池和Hibernate的二级缓存(不讲)。是线程安全的对象。一个项目创建一个对象即可。

我的理解——SessionFactory它不是轻量级的,所以不要频繁创建关闭它。在一个项目中有一个SessionFactory就可以了,通过SessionFactory来获取Session进行操作。那么问题来了,怎样可以保证在一个项目中所使用的SessionFactory是同一个哪? 
可在cn.itheima.utils包下创建一个工具类——HibernateUtils.java,其代码为:

7.3 Session:类似Connection对象是连接对象

 

Session代表的是Hibernate与数据库的链接对象。不是线程安全的。与数据库交互桥梁。

 

      Session中的API

       7.3.1保存方法:

      Serializable save(Object obj);

 

      7.3.2查询方法:

    T get(Class c,Serializable id);

    T load(Class c,Serializable id);

    get方法和load方法的区别?

   get方法
* * 采用的是立即加载,执行到这行代码的时候,就会马上发送SQL语句去查询。
*      * 查询后返回是真实对象本身。
* * 查询一个找不到的对象的时候,返回null

    load方法:

* * 采用的是延迟加载(lazy懒加载),执行到这行代码的时候,不会发送SQL语句,当真正使用这个对象的时候才会发送SQL语句。
*  * 查询后返回的是代理对象。javassist-3.18.1-GA.jar 利用javassist技术产生的代理。

*  * 查询一个找不到的对象的时候,返回ObjectNotFoundException

7.3.3 修改方法

 

void update(Object obj);

方法1:直接创建对象,进行修改
/*Customer customer = new Customer();
customer.setCust_id(1l);
customer.setCust_name("王聪");
session.update(customer);*/

 

方法2:先查询,再修改(推荐)

Customer customer = session.get(Customer.class, 1l);
customer.setCust_name("王小贱");

 

session.update(customer);

 

7.3.4 删除方法

 

voiddelete(Object obj);

方法1: 直接创建对象,删除
/* Customer customer = new Customer();
customer.setCust_id(1l);
session.delete(customer);*/

 

方法2:// 先查询再删除(推荐)--级联删除

Customer customer = session.get(Customer.class, 2l);

session.delete(customer);

7.3.5 保存或更新

void saveOrUpdate(Object obj)

方法1:

                /*Customer customer  = new Customer();

customer.setCust_name("王凤");
session.saveOrUpdate(customer);*/
方法2:
Customer customer = new Customer();
customer.setCust_id(3l);   //有id更新
customer.setCust_name("李如花");

 

session.saveOrUpdate(customer);

 

7.3.6 查询所有

 

方法1:

                // 接收HQL:Hibernate Query Language 面向对象的查询语言

/*Query query = session.createQuery("from Customer");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}*/
方法2:
// 接收SQL:
SQLQuery query = session.createSQLQuery("select * from cst_customer");
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));

 

}

 

 

第二讲:Hibernate主键生成策略--一级缓存--事务管理

 

1.1 什么是持久化类

 持久化:将内存中的一个对象持久化到数据库中的过程。Hibernate框架就是用来进行持久化的框架。

 持久化类:一个java对象与数据库的表建立了映射关系,那么这个类在Hibernate中称为是持久化类。

 持久化类=java类+映射文件

1.2 持久化类的编写规则

 

l  对持久化类提供一个无参数的构造方法:   Hibernate底层需要使用反射机制生成实例

属性需要私有,对私有属性提供public的get和set方法:  Hibernate中获取,设置对象的值。

l  对持久化类提供一个唯一标识符OID与数据库主键对应:   java中通过对象的地址区分是否是同一个对象,数据库中通过主键确定是否是同一个记录,在Hibernate中通过持久化类的OID的属性区分是否是同一个对象。

l  持久化类中的属性尽量要使用包装类类型来定义:  因为基本数据类型默认值是0,那么0就会有很多的歧义。但是包装类类型默认值是null.

l  持久化类不要使用final进行修饰:   延迟加载本身是Hibernate一个优化手段。返回的是一个代理对象(javassist,可以对没有实现接口的类产生代理---使用了非常底层字节码增强技术,继承这个类来代理),如果不能被继承,不能产生代理对象,延迟加载也就失效,Load和get就一致了。

1.3 主键生成规则

 

1.3.1 主键的分类

1.3.1.1 自然主键

l  自然主键:主键的本身就是表中的一个字段(实体中的一个具体属性)

 创建一个人员表,人员都会有一个身份证号(唯一的不可重复的),使用了身份证号作为主键。

1.3.1.2 代理主键

l  代理主键:主键的本身不是表中必须的一个字段(不是实体中的一个具体属性)

n  创建一个人员表,没有使用了人员的身份证号,用了一个与这个表不相关的字段ID,(PNO)。这种主键就是代理主键。

在实际开发中尽量使用代理主键

一旦自然主键参与到业务逻辑中,后期就有可能需要修改源代码。

一个好的程序设计要满足OCP原则,对程序的扩展是open的,对修改源代码是close的。

1.3.2 主键的生成策略

在实际开发中一般不允许用户手动设置主键,一般将主键交给数据库,手动编写程序进行设置。在Hibernate中为减少程序编写,提供了很多的主键生成策略。

l  increment: hibernate中提供的自动增长机制,适用于short、int、long类型的主键。在单线程程序中使用。

n  首先发送一条语句,select max(id) from 表; 然后让id+1作为下一条记录的主键。

l  identity:适用于short、int、long类型的主键,使用的是数据库底层的自动增强机制。适用于有自动增强机制数据库(MySQL,MSSQL),但是Oracle是没有自动增长。

l  sequence: 适用于short、int、long类型的主键,采用的是序列的方式。(Oracle支持序列)。

l  uuid:  适用于字符串类型主键。使用hibernate中的随机方式生成字符串主键。

l  native: 本地策略,可以在identity和sequence之间进行自动切换。

l  assigned:  hibernate放弃外键的管理,需要通过手动编写程序或者用户自己设置。

l  foreign:  外部的,一对一的一种关联映射的情况下使用。

 

1.4持久化类的三种状态

1.4.1持久化类的三种状态

Hibernate是持久层框架,通过持久化类完成ORM操作。Hibernate为了更好的管理持久化类,将持久化类分成三种状态。

持久化类=java类+映射

1.4.1.1 瞬时态

l  这种对象没有唯一的标识OID,没有被session管理,称为是瞬时态对象。

1.4.1.2 持久态

这种对象有唯一的标识OID,被session管理,称为是持久态对象。

持久化类的持久态的对象,可以自动更新数据库。

1.4.1.3 脱管态

l  这种对象有唯一的标识OID,没有被session管理,称为是脱管态对象。

1.4.1.4 如何区分这3中状态

1.4.2 持久化类的状态转换(了解)

1.4.2.1 三种状态的转换图

1.4.2.2 瞬时态对象

l  瞬时态对象

n  获得瞬时态对象

u  Customercustomer = new Customer();

n  状态转换

u  瞬时->持久

save(Object obj)、saveOrUpdate(Objectobj)

u  瞬时->脱管

              customer.setCust_id(1l);

1.4.2.3 持久态对象

l  持久态对象

n  获得

u  get()、load()、find()、iterate()

u  Customercustomer = session.get(Customer.class, 1l);

n  状态转换

u  持久->瞬时

delete()

u  持久->脱管

close()、clear()、evict(Objectobj)、

 

1.4.2.4 脱管态对象

l  脱管态对象

n  获得

u  Customercustomer = new Customer(); customer.setCust_id(1l);

n  状态转换

u  脱管->持久

update()、saveOrUpdate()

u  脱管->瞬时

customer.setCust_id(null);

1.4.3 持久态对象特性

1.4.3.1 持久化类持久态对象自动更新数据库

@Test

       //持久态对象自动更新数据库

       publicvoid demo2() {

              Session session = HibernateUtils.openSession();

              Transaction tx = session.beginTransaction();

             

              //获得持久化对象,该对象可以自动更新数据库

              Customer customer = session.get(Customer.class, 1l);

              customer.setCust_name("王南");

              //session.update(customer);

             

              tx.commit();

              session.close();

       }

1.5Hibernate的一级缓存

1.5.1 缓存的概述

1.5.1.1 什么是缓存

缓存:一种优化的方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源。

1.5.2 Hibernate的缓存

1.5.2.1 Hibernate的一阶缓存

Hibernate框架中提供了优化手段:缓存和抓取策略。Hibernate中提供了两种缓存机制:一级缓存、二级缓存。

Hibernate的一级缓存:称为是Session级别的缓存,一级缓存生命周期与Session一致(一级缓存是由Session中的一系列的java集合构成)。一级缓存是自带的不可卸载的。(Hibernate 的二级缓存是SessionFactory级别的缓存,需要配置的缓存)。

1.5.2.2 证明一级缓存的存在

publicclass HibernateDemo3 {

   @Test

       //证明一级缓存的存在

       publicvoid demo1() {

              Session session = HibernateUtils.openSession();

              Transaction tx = session.beginTransaction();

             

              /*Customer customer1 =session.get(Customer.class, 1l);  //发送SQL语句

              System.out.println(customer1);

             

              Customer customer2 =session.get(Customer.class, 1l);  //不发送SQL语句

              System.out.println(customer2);

             

              System.out.println(customer1==customer2);

              */

             

              Customer customer = new Customer();

              customer.setCust_name("凤姐");

              Serializable id= session.save(customer);

              Customer customer2 = session.get(Customer.class, id); //不发送SQL语句

              System.out.println(customer2);

             

              tx.commit();

              session.close();

       }

}

1.5.3 Hibernate的一级缓存的内部结构

1.5.3.1 一级缓存中特殊区域:快照区

@Test

       //一级缓存的清空

       publicvoid demo3() {

              Session session = HibernateUtils.openSession();

              Transaction tx = session.beginTransaction();

             

              Customer customer1 = session.get(Customer.class, 1l); //发送SQL语句查询,同时放入到一级缓存中

              session.clear(); //清空所有

              session.evict(customer1);  //清除一个对象

             

              Customer customer2 = session.get(Customer.class, 1l); //发送SQL语句查询

              System.out.println(customer1);

              System.out.println(customer2);

             

             

              tx.commit();

              session.close();

       }

 

 

1.6Hibernate的事务管理

1.6.1 事务的回顾

1.6.1.1 什么是事务

l  事务:事务指的是逻辑上的一组操作,组成这组操作的各个逻辑单元要么全都成功,要么全都失败。

1.6.1.2 事务特性

l  原子性 :  代表事务不可分割

l  一致性: 代表事务执行的前后,数据的完整性保持一致。

l  隔离性:代表一个事务执行的过程中,不应该受到其他事务的干扰、

l  持久性:代表事务执行完成后,数据就持久到数据库中。

1.6.1.3 如果不考虑隔离性,引发安全性问题

l  读问题

n  脏读: 一个事务读到另一个事务未提交的数据。

n  不可重复读: 一个事务读到了另一个事务已经提交的update数据,导致在前一个事务多次查询结果不一致。

n  虚读: 一个事务读到了另一个事务已经提交的insert数据,导致在前一个事务多次查询结果不一致。

l  写问题(了解)

n      引发两类数据更新

1.6.1.4 读问题的解决

l  设置事务的隔离级别

n  Readuncommitted: 以上读问题都会发生

Read committed: 解决脏读,但是不可重复读和虚读都有可能发生

Repeatable read: 解决脏读和不可重复读,但是虚读有可能发生

n  Serializable: 解决所有问题

1.6.2 Hibernate中设置事务隔离级别

1.6.2.1 Hibernate中设置事务隔离级别

<!-- 设置事务隔离级别 -->

<property name="hibernate.connection.isolation">4</property>

 

1.6.3 Service层事务

1.6.3.1 Hibernate解决Service的事务管理

l  改写工具类

public class HibernateUtils {


public static final Configuration cfg;
public static final SessionFactory sf;

static{
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}

public static Session openSession(){
return sf.openSession();
}

public static Session getCurrentSession() {
return sf.getCurrentSession();
}
}

<!-- 配置当前线程绑定的Session -->

这是获取一个与线程绑定的Session(即线程安全的Session)。

<property name="hibernate.current_session_context_class">thread</property>

 

Hibernate中常用API-Session的补充

讲完Hibernate持久化对象的三种状态和一级缓存之后,我就可以继续深入一点的讲解Session类中的以下方法了。

update

udpate操作主要是针对于脱管对象而言的,因为持久化对象具有自动更新数据库的能力。如果我们直接操作的对象是一个脱管对象,执行update会出现什么情况?

得出结论:update操作时,如果操作的对象是一个脱管对象,则可以操作,并且它会将脱管对象转换成持久对象再操作

 

第三讲   Hibernate表操作-多对多配置

1.1

1.2创建映射文件

 

l  多的一方的映射的创建

<!-- 配置多对一的关系:放置的是一的一方的对象 -->
<!-- 
many-to-one标签
* name :一的一方的对象的属性名称。
* class :一的一方的类的全路径。
* column :在多的一方的表的外键的名称。
-->
<many-to-one name="customer"  class="com.itheima.hibernate.domain.Customer" column="lkm_cust_id"/>

 

l  一的一方的映射的创建

<!-- 配置一对多的映射:放置的多的一方的集合 -->
<!-- 
set标签 :
* name :多的一方的对象集合的属性名称。
* cascade:级联
* inverse:放弃外键维护权。
-->
<set name="linkMans"  >
<!--
key标签
* column:多的一方的外键的名称。
-->
<key column="lkm_cust_id"/>
<!-- 
one-to-many标签
* class :多的一方的类的全路径
-->
<one-to-many class="com.itheima.hibernate.domain.LinkMan"/>
</set>

1.3 测试

// 创建两个客户
Customer customer1 = new Customer();
customer1.setCust_name("王东");
Customer customer2 = new Customer();
customer2.setCust_name("赵洪");

// 创建三个联系人
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("凤姐");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("如花");
LinkMan linkMan3 = new LinkMan();
linkMan3.setLkm_name("旺财");

// 设置关系:
linkMan1.setCustomer(customer1);
linkMan2.setCustomer(customer1);
linkMan3.setCustomer(customer2);
customer1.getLinkMans().add(linkMan1);
customer1.getLinkMans().add(linkMan2);
customer2.getLinkMans().add(linkMan3);

//3次联系人插入,2次客户插入,3次联系人更新(联系人对象发起,通过快照去和一级缓存区对比发现不一样),3次联系人发起(客户发起)
// 保存数据:
session.save(linkMan1);
session.save(linkMan2);
session.save(linkMan3);
session.save(customer1);

session.save(customer2);

1.4 一对多关系只保存一边是否可以

public void demo2(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

Customer customer = new Customer();
customer.setCust_name("赵洪");


LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("如花");
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);

// 只保存一边是否可以:不可以,报一个瞬时对象异常:持久态对象关联了一个瞬时态对象。
// session.save(customer);  //保存的那个是持久态,但是没保存的是瞬时态
session.save(linkMan);

tx.commit();
}

报错:21:19:17,351 ERROR SessionImpl:2994 - HHH000346: Error during managed flush [object references an unsaved transient instance - save the transient instance before flushing: com.itheima.hibernate.domain.Customer]

1.5  级联保存或更新操作:
*  级联保存或更新操作:
*  * 保存客户级联联系人,操作的主体是客户对象,需要在Customer.hbm.xml中进行配置
*  * <set name="linkMans" cascade="save-update">

1.6 测试对象的导航
/**
* 测试对象的导航
* * 前提:一对多的双方都设置cascade="save-update"
*/
public void demo5(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

Customer customer = new Customer();
customer.setCust_name("李兵");


LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("凤姐");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("如花");
LinkMan linkMan3 = new LinkMan();
linkMan3.setLkm_name("芙蓉");

linkMan1.setCustomer(customer);
customer.getLinkMans().add(linkMan2);
customer.getLinkMans().add(linkMan3);

// 双方都设置了cascade
// session.save(linkMan1); // 发送几条insert语句  4条
// session.save(customer); // 发送几条insert语句  3条
session.save(linkMan2); // 发送几条insert语句  1条
 

tx.commit();

 

 

1.7 级联删除:

/**

* 级联删除:
* * 删除客户级联删除联系人,删除的主体是客户,需要在Customer.hbm.xml中配置
* * <set name="linkMans" cascade="save_update,delete">
*/
public void demo6(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 没有设置级联删除,默认情况:修改了联系人的外键,删除客户
/*Customer customer = session.get(Customer.class, 1l);
session.delete(customer);*/

// 删除客户,同时删除联系人
Customer customer = session.get(Customer.class, 1l);
session.delete(customer);

tx.commit();
}

1.8  一对多设置了双向关联产生多余的SQL语句

@Test
/**
* 将2号联系人原来归1号客户,现在改为2号客户
*/
public void demo8(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 查询2号联系人
LinkMan linkMan = session.get(LinkMan.class, 2l);
// 查询2号客户
Customer customer = session.get(Customer.class, 2l);
// 双向的关联
linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan);

tx.commit();
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值