hibernate总结

框架:什么是框架:我们写程序的时候,框架为我们封装了一部分的功能,我们使用少量的代码就可以实现这个功能。
hibernate框架:应用在javaee三层结构的dao 层,实现对数据库的crud操作,其底层就是jdbc,使用hibernate的好处就是不用写复杂的jdbc代码,不需要写复杂的sql语句。hibernate是开源轻量级的框架,现在使用的是5的版本。
ORM思想:Object Relational Mapping 对象关系映射
让实体类与数据库表进行一一对应关系
让实体类和数据库表进行对应,让实体类中的属性和数据库表中的属性一一对应。
不需要直接操作数据库表,而是操作实体类对象完成crud的操作。

框架搭建

1、导包
导入hibernate-release-5.3.1.Final\lib\required下面的所有jar包,并且导入hibernate-release-5.3.1.Final\lib\jpa-metamodel-generator下面的jar包
因为使用hibernate的时候有日志信息输出,hibernate本身没有日志输出的jar包,导入其他日志的jar包,并且还需要导入数据库驱动jar包
日志及驱动jar包

2、准备hibernate映射文件和核心配置文件
项目工程路径
在实体所在包下新建映射文件(名称:类名.hbm.xml),解压hibernate-release-5.3.1.Final\lib\required\hibernate-core-5.3.1.Final.jar,找到hibernate-mapping-3.0.dtd,即找到dtd约束,即下面的一段代码。
映射文件位置不是固定的,但一般放在实体类所在包中,而核心配置文件是固定的,即在src下面,名字为hibernate.cfg.xml。

<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>

User.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!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属性:实体类全路径
            table:数据库标的名称
        -->
        <class name="cn.lq.user.User" table="t_user">
        <!-- 配置类的id和表的id对应
            hibernate要求实体类中有一个属性唯一值
            hibernate要求表有字段作为唯一值(即主键)
         -->
         <!-- id标签
            name属性:实体类里面id属性
            column属性:生成表中的字段的名称,(一般都与id名称相同)            
          -->
         <id name="uid" column="uid">
            <!-- 设置数据库表id的增长策略
                native:生成表单id值是自动增长
             -->
             <generator class="native"></generator>
         </id>
         <!-- 配置其他的属性和表字段对应 这个里面有一个type属性,是设置数据库表字段的类型,
         可以不用设置,会自动进行生成 -->
        <property name="uname" column="uname"></property>
        <property name="password" column="password"></property>
        </class>
    </hibernate-mapping>

3、准备主配置文件
在src目录下,新建hibernate.cfg.xml主配置文件,导入dtd约束(在hibernate-configuration-3.0.dtd),其中的很多name的值不需要记忆会找即可
hibernate。properties配置文件在安装目录hibernate-release-5.3.1.Final\project\etc下面;
注意:一定要把映射文件配置到核心映射文件中去

<mapping resource="cn/lq/user/User.hbm.xml"/>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- 配置数据库信息 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hibernate1</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>  
        <!-- 配置hibernate信息 可选的 -->
        <!-- 输出底层sql语句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 输出底层sql语句格式化 -->
        <property name="hibernate.format_sql">true</property>
        <!-- hibernate帮创建表,需要在配置之后 
            update:如果有表,更新,如果没有表就创建-->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- 配置数据库的方言  -->
        <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
        <!-- MySql5.0之前的配置
        <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
        5.0之后需要使用
        <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> -->
        <!-- 把映射文件配置到核心文件中 
            这里要写的是文件路径,所以用的是反斜杠(/),而之前写的是类路劲,用的是点(.)
            路径从与该配置文件对应的路径开始写
            -->
        <mapping resource="cn/lq/user/User.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

4、实现添加操作

  1. 加载hibernate核心配置文件
  2. 创建SessionFactory对象
  3. 使用SessionFactory创建session对象
  4. 开启事务
  5. 写具体逻辑crud事件(除了这一步,其余都是固定的)
  6. 提交事务
  7. 关闭资源
package cn.lq.Test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

import cn.lq.user.User;

public class HibernateDemo {
    @Test
    public void add(){
//       1. 加载hibernate核心配置文件
        //实现:在src下面找到hibernate.cfg.xml,创建Configuration对象,然后再把配置文件封装对象里
        Configuration cfg = new Configuration().configure();

//       2. 创建SessionFactory对象
        //读取hibernate核心配置文件内容,创建SessionFactory
        //在这一过程中,根据映射关系,在配置数据库中把表创建
        SessionFactory sessionFactory = cfg.buildSessionFactory();

//       3. 使用SessionFactory创建session对象
        //类似于连接
        Session session = sessionFactory.openSession();

//       4. 开启事务
        Transaction tx = session.beginTransaction();
//       5. 写具体逻辑crud事件(除了这一步,其余都是固定的)
        User user = new User();
        user.setUname("Sakara");
        user.setPassword("123456");
        //调用session的方法来进行添加。
        session.save(user);
//       6. 提交事务
        tx.commit();
//       7. 关闭资源
        session.close();
        sessionFactory.close();
    }
}

创建sessionFactory是特别耗费资源的操作,所以应该只创建一次,应该是单实例对象,解决办法就是:创建创建一个工具类,使用静态代码块的方式实现。
session单线程对象,不能共用,每个人用的都是自己的对象。

public class SessionFactoryUtils {  
    private static final Configuration cfg;
    private static final SessionFactory sessionFactory; 

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

    public static SessionFactory getSessionFactory(){
        return sessionFactory;
    }
}

5、id的增长策略
(1)native:会自动匹配你的数据库,进行自动增长
(2)uuid:需要实体类中id的类型为字符串类型

crud操作

1、添加

        User user = new User();
        user.setUname("Sakara");
        user.setPassword("123456");
        //调用session的方法来进行添加。
        session.save(user);

2、根据id进行查询(使用session中的get()方法)

        //第一个参数:实体类的class,第二个参数:要查询的id的值
        User user = session.get(User.class, 1);
        System.out.println(user);

3、修改(使用session中的update())

        //先根据id进行查询,然后再进行修改
        User user = session.get(User.class, 1);
        //设置要修改的值
        user.setUname("楚子航");
        //使用session中修改的方法
        session.update(user);

        //另一种方式
        User user = new User();
        user.setUid(2);
        user.setUname("诺诺");
        session.update(user);
        //虽然会执行成功,但是在数据库中,password的值会为空,所以通常还是使用先查询再修改的方式进行

4、删除(使用session中的delete())

        //先根据id进行查询,然后再进行删除
        User user = session.get(User.class, 1);     
        session.delete(user);
        //另一种方式(不常用)
        User user = new User();
        user.setUid(2);
        session.delete(user);

save(),update(),saveOrUpdate()区别

实体类的三种状态
实体类的编写建议使用基本类型对应的包装类
原因如下:一个学生的分数是0,可以写int=0,但是如果学生没有考试,那分数应该是null,而不是0,但是int=null是错误的。
1、瞬时态:对象里没有id,对象和session没有关联(通常是在添加之前的时候存在)

        User user = new User();
        user.setUname("Sakara");
        user.setPassword("123456");
        //在save之前是属于瞬时态
        session.save(user);

2、持久态:对象里面有id值,并且和session有关联(根据id查询)

User user = session.get(User.class, 1); 
//对象有id值,并且是从session中查出来的

3、托管态:对象有id值,但是和session没有关系

        User user = new User();
        user.setUid(2);

saveOrUpdate():
1、如果实体类对象为瞬时态,该方法进行的是添加操作
2、如果实体类对象为托管态,该方法进行的是修改操作
因为id是唯一的,如果设置的是自动增长,当你把id=1的记录删除后,然后又将托管装态的id=1的对象使用saveOrUpdate()添加到数据库中,虽然可以执行成功(此时执行的仍为修改操作),但是数据库中不会有记录。
3、如果实体类对象为持久态,该方法进行的是修改操作


什么是缓存?
数据存到数据库里面,数据库本身是一个文件系统,对于文件的操作需要时用流来进行操作,当数据量比较多的时候,使用流的效率并不是很高。
1、把数据存到内存里面不需要使用流的方式,可以直接读取内存中的数据
2、把数据放到内存中,提高读取速率

hibernate有很多优化方式,hibernate的缓存就是一种优化方式
(1)hibernate的一级缓存默认是打开的
(2)hibernate的一级缓存使用范围,是session的范围,从session创建开始到session关闭结束
(3)hibernate的一级缓存,存储数据必须是持久态数据
现在已经使用redis替代了二级缓存。

hibernate的一级缓存执行过程:
在同一个session中两次查询同为id=6的数据,执行时只有第一次会在控制台显示sql语句,第二次不会出现sql语句
执行第一次查询的时候,会首先查询一级缓存,发现一级缓存没有内容,然后查询数据库。返回user1的对象(持久态数据),并保存到一级缓存中,执行第二次查询的时候,查询一级缓存的内容,发现有数据,直接返回。
另外在一级缓存中,存放的不是一个对象,而是对象中的每一个值,然后把这些值存放到一个区域,给一个名字,即id=6。下次在根据id=6进行查找的时候,是根据id来判断,然后把区域中的内容组成一个对象返回。

hibernate一级缓存的特性:
持久态会自动更新数据库,

  User user = session.get(User.class, 1);       
        user.setUname("楚子航");
        //因为查出来的user是持久态,所以不用调用下面的方法也可以更新数据库
        //session.update(user);

其实一级缓存分为两个部分,一个是一级缓存,另一部分是一级缓存快照区,即一级缓存副本。根据id查询的时候,会把查询结果分别放入两个地方。当执行第二步设置名字时,会修改一级缓存中的内容,但是不会修改快照区的内容,在最后提交事务的时候,会对比一级缓存和快照区的内容,如果两者内容不一致,则将一级缓存内容更新到数据库中。

为了保证session是一个单线程对象,可以将其与本地线程绑定,需要在核心配置文件中进行配置,然后获取session的时候,getCurrentSession(),使用这个session,不用手动关闭,因为本地线程关闭的时候,会把session也关闭了。再次关闭会报错。

查询数据使用的三种方式
1、Query对象,这个不需要使用sql(操作表和字段)语句,但是需要使用hql(操作类和属性)语句,查询所有的hql语句:from 实体类的名称

//首先创建Query对象,然后调用query对象的方法。
   Query query = session.createQuery("from User");
            List<User> list = query.list();
            for(User user : list){
                System.out.println(user);
            }

2、Criteria 方式

//创建Criteria对象,方法的参数是你实体类,调用其方法。
Criteria criteria = session.createCriteria(User.class);
            List<User> list = criteria.list();
            for (User user : list) {
                System.out.println(user);
            }

3、sqlQuery方式
//调用方法后,返回的是list集合,集合里面有多个值,默认的是数组

SQLQuery sqlQuery = session.createSQLQuery("select * from t_user");
            sqlQuery.addEntity(User.class);
            List<User> list = sqlQuery.list();
            for(User user : list){
                System.out.println(user);
            }

hibernate一对多及多对多映射关系配置

hibernate一对多
以客户和联系人为例,客户是1,联系人是多
1、创建两个实体类,客户和联系人
2、让两个实体类之间相互表示
(1)在客户实体类中表示多个联系人,使用集合来进行表示,但是hibernate中要求我们使用set集合,一是无序,二是可以重复
(2)在联系人实体类中表示客户,使用客户类实体类
3、配置映射关系
(1)一般有几个实体类,就有几个配置文件
(2)先进行基本的配置
(3)进行两个实体类之间的关系配置

<!-- 在客户映射文件中表示所有联系人 
            使用set标签表示所有联系人,set标签中有name属性,
                属性是写在客户实体类里面表示联系人集合的名称
        -->
        <set name="setLinkMan">
            <!-- 一对多建表有外键
                hibernate机制:双向维护外键,在一和多的一方都配置外键
                column属性值:外键名称
             -->
             <key column="clid"></key>
             <!-- 客户所有的联系人,class里面写联系人实体类的全路径 -->
             <one-to-many class="cn.lq.domain.LinkMan"/>
        </set>
<!-- 表示联系人所属客户
            name属性:因为联系人实体类使用customer对象表示,写customer名称
            class属性:customer全路径
            column属性:外键名称,需要和Customer.hbm.xml中的保持相同的名字
         -->
         <many-to-one name="customer" class="cn.lq.domain.Customer" column="clid"></many-to-one>

4、创建核心配置文件,引入映射文件

<!-- 把映射文件配置到核心文件中 
            这里要写的是文件路径,所以用的是反斜杠(/),而之前写的是类路径,用的是点(.)
            路径从与该配置文件对应的路径开始写
            -->

一对多的级联操作
1、级联保存
(1)添加客户,为这个客户添加多个联系人
复杂写法


//第一步:创建实体类
            Customer customer = new Customer();
            customer.setCustName("传智播客");
            customer.setCustSource("Internet");
            customer.setCustLevel("VIP");
            customer.setCustPhon("185326");
            customer.setCustMobile("789456");

            LinkMan linkMan = new LinkMan();
            linkMan.setLkm_name("路明非");
            linkMan.setLkm_gender("男");
            linkMan.setLkm_phone("1175645");

            //建立两者之间的联系
            customer.getSetLinkMan().add(linkMan);
            linkMan.setCustomer(customer);
            //保存到数据库中
            session.save(customer);
            session.save(linkMan);

简化写法
首先需要在客户的映射文件中的set标签上cascade属性进行配置

<set name="setLinkMan" cascade="save-update">

第二步:创建客户和联系人,只需要把联系人添加到客户中就可以了,最终只需要保存客户即可。(也不需要把客户再放到联系人中)

2、级联删除
(1)删除某一个客户,这个客户里面的所有联系人都删除
第一步:在客户映射文件set标签中进行配置,设置delete

<set name="setLinkMan" cascade="save-update,delete">

第二步:在代码中直接删除客户
删除方法:先根据id查询对象,再使用session中的delete方法。

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

执行步骤:
(1)根据id查询客户对象
(2)根据查到的外键的值来查询联系人
(3)将对应的联系人的外键设为null
(4)删除联系人
(5)删除客户

3、级联修改
路明非本来是属于传智播客(为传智播客打个广告)的联系人,但是他跳槽到了卡塞尔学院,此时需要做的就是外键的引用进行更改指向卡塞尔学院即可。

        //根据路明非的id来得到路明非这个对象
            LinkMan lmf = session.get(LinkMan.class, 1);
            //根据卡塞尔学院的id来得到卡塞尔学院这个对象
            Customer kse = session.get(Customer.class, 2);
            //将路明非添加到卡塞尔学院的联系人中
            kse.getSetLinkMan().add(lmf);
            //将卡塞尔学院添加到路明非的客户属性中
            lmf.setCustomer(kse);
            //这样就执行完毕不用调用保存或者更新方法,
            //因为上面都是持久态数据,会自动保存

4、级联修改中inverse属性
Hibernate中使用的是双向维护外键,在修改客户的时候修改了一次外键,修改联系人的时候又修改了一次外键。所以在上面的修改中会出现两次update操作,这样效率低,所以要让一方放弃外键维护。因为这个是一对多,所以要让一的那一方放弃外键维护,在映射文件中,set标签内,配置inverse属性,值为true(放弃),而默认值是false(不放弃)

多对多的操作
这个时候虽然有两个实体,但是有3个表,第三张表示两个实体之间的关联表。
User和Role,两个类里面分别使用set集合存储对方的实体类,并且声称set,get方法。
1、映射文件的配置
基本操作配置完成以后,要在两个表映射文件中分别配置关联配置

<!-- 在角色映射文件中表示所有联系人 
            使用set标签表示所有角色,set标签中有name属性是写在客户实体类里面表示联系人集合的名称
            table:第三张表的名字
        -->
        <set name="setRole" table="user_role" cascade="save-update,delete" inverse="true">
            <!-- 
                column属性值:当前映射文件在第三张表的外键的名称
             -->
             <key column="ur_uid"></key>
             <!-- class里面写角色实体类的全路径   column:角色在第三张表中外键的名称 -->
             <many-to-many class="cn.lq.domain.Role" column="ur_role"></many-to-many>
        </set>

对应关系

注意:要引入到核心配置文件中

2、级联保存与级联删除
级联保存:和一对多相同,创建User,创建Role,将角色保存到User中,然后保存User即可.
级联删除:根据id,找到User,然后session.delete(User),级联删除一般不用。

用户和角色的关系是通过第三张表进行维护的。
让某个用户拥有某个角色:(1)、根据id查到用户和角色,然后把角色放到用户的set集合里user.getSetRole.add(role)
让某个用户没有某个角色:根据id查到用户和角色,然后从用户set集合里移除某个角色user.getSetRole.remove(role)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值