刘悦栋SSH史上最牛逼的SSH三大框架讲解之Hibernate
https://www.kanbilibili.com/video/av21895930/index_11.html
IDEA自动生数据库中table对应的实体类和hibernate的映射文件
注意有坑:这样会修改你的hibernate.cfg.xml文件。
sql99:
DDL:数据库定义(Definition)语言,database table column的crud
DML:数据库操纵(Manipulation)语言,table中行的crud
DCL:数据库控制(Control)语言,权限,事务
sessionFactory
① 耗费资源较大
从Hibernate配置文件可以看出主要就是配置sessionFactory
② 线程安全的(获得的各个session独立)
new Configuration().buildSessionFactory().openSession()得到一个新session
getCurrentSession()就是获得当前线程绑定的session
hibernate提供的增删改查:
save(Object)
对应sql的一次insert,相当于把对象的各个属性拿出来,对应到insert语句中:
INSERT into tableName(col1,col2...coln) values(value1,value2...valuen);
上面的tableName通过解析entity.hbm.xml或者注解得到,(col1,col2...coln) 和(value1,value2...valuen)的映射关系也通过entity.hbm.xml得到(一个对象的各个属性(getter得到)insert到表中哪些列中去),不过对于主键列要看entity.hbm.xml的generator的策略,一般都是native或identity,即使用数据库的自增属性,那么插入时候的sql (col1,col2...coln)的列中是没有id列的,让数据库自动自增。
select(args)
对应sql的一次select
一般而言使用的都是id查询,过程和save差不多
update(Object)
注意参数是Object类型,没有id选项!!
真正实现修改需要session.get方法,和session.update方法
所以对应sql的一次select和一次update
因为本身hibernate update的过程就是先get(具体哪种参数的get不定),用对象接收,利用setter方法修改对象属性,再save对象:
Student stu = session.get(Student.class,5);
stu.setCity("Beijing");
session.save(stu);
这样做的性能太差了:
① get的过程复杂性就未知,需求是修改特定id用户的信息还好,因为get的过程直接调用session的getById(int id)这个API即可,但是如果是修改所有城市为Shanghai的用户,那么get的过程可能就很复杂
当然有个思路:前台无论需要什么数据,只要会设计到后续修改的,全都把id传过去,这样在修改时可以传回id,get过程保证都能用getById(int id)这个API,但是这样做的坏处是:一来信息冗余,多处理了信息(其实可以忽略嘛,一个id而已),二来这个信息肯定一直到了前台,暴露了用户在数据库中的真实信息,这个就特别不好了
② 通过session.get拿到该对象之后,是使用实体类的setter方法“修改”该值,再调用session.update()方法,过程肯定和select和delete方法一样,拿着修改之后的对象,到数据库使用sql语句的update
这个过程中,明显发现,原本一条简单的update的语句,变成了先session.get()原始数据库row——封装成对象(反射机制)——使用该对象的setter方法完成“修改”——session.update()方法传入相应修改值到数据库——执行update方法。
开销太大了,使用Mybatis只用做最后一步的工作——送sql——update语句过去,而hibernate来来回回做了很多“开销”工作,为的就是所谓的面向对象编程。
delete(Object)
和update类似,需要先get出来,再delete。。。
Student stu = session.get(Student.class,5);
session.delete(stu);
换个思路,看下hibernate在上述代码执行的sql:
hibernate执行的sql:
可以看到,get主要是找到该记录的id,接下来delete其实就只用到了id这个参数。而其实当时get的时候就是利用id来get的(当然可以使用其他get API),这给了一点提示——我们直接拿着id自己创建对象去delete这个对象不就行了,动手:
表:
代码:
sql和结果:
表:
竟然真的删除了,而且由于我们上面没有设置name属性,执行delete时该属性应该还是默认null。这又提示我们:即使设置一个数据库表中没有对应name的Student对象,一样可以删除,动手:
结果:
表:
我们new 出来的temp设置的是id = 2,name = "这个name不存在",数据库中并没有这样的一条记录,但是session.delete()执行的sql记录可以看到,delete只用了id这个属性,故一样可以删除id = 2的属性。
明显这个方法可以减少一个数据库查询操作
主键:
自然主键:如身份证号
代理主键:本身没有任何实际意义,只是用来标识唯一性而已(满足主键要求)
类的属性:
有setter和getter方法就是属性,没有的就不是属性,这是充要条件
对象的三种状态:
使用getCurrentSession()的注意点:
① 在hibernate.cfg.xml中配置hibernate_current_session_context_class属性:thread
不然直接使用getCurrentSession()可能获得的不是绑定的session
② 使用getCurrentSession()获得的session,在事务提交后不用手动关闭(不同于openSession()得到的session),一旦事务提交hibernate会自动关闭session。
配置数据库隔离级别和getCurrentSession()的线程绑定:
session仅有的几个API都只是“等值查询”——例如getById,对于批量查询,hibernate使用下面三种策略:
先看hql查询;
hql思想:面向对象的操作,hql中全是类名,属性名。不会涉及到表,由hibernate作到表的映射:
① from类的全限定名,而不是表
② 如果是select * (行的全部列)则select *可以省略
条件查询:
可以看到,cust_id也是Customer类的属性名。
明显,坏处是hql还要经解析xml才能转化为数据库能识别的sql,好处就是返回结果就是对象,而不是字符串。
当然,还有传入参数的hql:问好占位符+setParameter完成传参:
当然,还有一种方式:
这种方式不用管参数在hql中的位置,利用起的名字(随便起,只要几个参数名不重复即可)去set,达到传入参数的目的。
hql分页:
criteria:
基本查询,传入Class对象:
条件查询:
将原本的sql语句换成Restrictions的各种静态方法,具体对应关系如下:
criteria的分页:
和hql差不多,具体为什么hql和criteria不支持原生sql语法+setParameter()我也不知道。。。
使用Projections的静态方法调用聚合函数:
原生sql;
基本查询:
注意用List<Object[]>接收query.list()(其实就是执行sql)的结果,因为hibernate无法识别cst_customer是什么,也不会尝试去解析,只是而且会把执行sql的结果(从数据库接收)的每一行封装成一个对象数组,n行放在List。我们接收时也只能用Object[]来接收,因为一行中的不同列类型是不一样的。
当然,我们可以手动告诉hibernate把每一行(Object[])装进什么类型中:
条件查询:
其实关键问题就是怎么进行参数传入而已:
跟hql一样,都是使用setParameter()方法,而且下标一样从0开始(不同于JDBC的条件查询,从1开始)。
分页查询:
很舒服,就是使用limit+setParameter完成参数传递。