* hibernate简介:
*
* 1.Transient Objects:瞬时对象
* 2.Persistent Objects:持久对象
* 3.session:hibernate的会话
* 4.SessionFactory:会话工厂,进程级别的对象,重量级的
* 5.Transaction:本地事务
* 6.TransactionFactory:事务工厂
* 7.ConnectionProvider:使用第三方数据源,如c3p0,dbcp
* 8.JNDI:java name Directory interface:提供资源目录
* 9.JDBC:操作数据库的api
* 10.JTA:跨数据库的事务
*
* 1.1.3每张table表的映射文件:Event.hbm.xml
* 映射文件--告诉hibernate--访问数据库里面的那个表以及应该使用表里的那些字段
* <hibernate-mapping package="">
* <class name="Event" table="EVENTS">
* <id name="id" column="EVENT_ID">
* <generation class="native"/>
* </id>
* <property name="date" type="timestamp" column="EVENT_DATE"/>
* <property name="title"/>
* </class>
*</hibernate-mapping>
*1.timestape列用于insert或update操作时记录日期和时间
*2.如果你不分配一个值,表中的第一个timestamp列自动设置为最近操作的日期和时间
*3.也可以通过分配一个null值,将timestamp列设置为当前的日期和时间
*4.timestamp值返回后显示为YYYY-MM-DD HH:MM:SS格式的字符串
*5.package:指定当前映射文件的实体类model所在的包
*
* 1.1.4hibernate的核心配置文件:hibernate.cfg.xml
* <hibernate-configuration>
* <session-factory>
* <property name=""></property>
* ................
* 数据库连接设置
* 可选配置
* 设置数据库连接池的初始化连接数
* <mapping resource="com/rl/hiber/model/Event.hbm.xml"/>
* </session-Factory>
* </hibernate-configuration>
*
* 1.如果使用对个数据库,就要用多个<session-factory>,通常放在多个配置文件中,更容易启动
*
* 生成表::
* 1.创建hibernate的配置对象
* Configuration cfg=new Configuration();
* 2.指定hibernate核心配置文件:hibernate.cfg.xml的位置
* cfg.configura("hibernate.cfg.xml");
* 3.创建表的对象
* SchemaExport se=new SchemaExport(cfg);
* se.create(true,true);
*
* 测试--插入数据
*1.创建hibernate的配置对象
*Configuration cfg=new Configuration();
*2.指定hibernate核心配置文件的位置
*cfg.configure("hibernate,cfg.xml");
*3.注册配置属性信息
*ServiceRegistry sr=new StandardServiceRegistryBuilder()
* .applySettings(cfg.getPropertirs()).build();
*4.创建SessionFactory
*SessionFactory factory=cfg.buildSessionFactory(sr);
*5.创建session
*Session session=factory.openSession();
*6.开启事务
*Transaction tx=session.beginTransaction();
*7.创建对象User,并给对象赋值
*8.通过session保存对象
*session.save(user);
*9.提交事务
*tx.commit();
*10.关闭session
*session.close();
*
*模型类的规范:
*1.必须要有默认构造方法:查询时把数据表中的一条数据映射成一个对象时需要使用默认的构造器来创建对象
*2.必须提供一个OID,作为对象的主键
*3.属性的私有化封装,提供set和get方法
*4.不要使用final来修饰模型类中的成员,如果是final类型后续延迟加载无法实现
*5.最好使用包装类
*
*属性映射
*property:属性映射
*属性:
* name:模型类的属性名
* column:映射到表中的列名
* type:hibernate提供的数据类型,可以由java的数据类型转换成数据库中的数据类型
* length:映射到数据库中字段的长度
* not-null:是否可以为空
* unique-key:唯一约束的键名
*主键映射策略:
* assigned:手动指定id,在实际项目中基本不用
* increment:通过获得当前的id的最大值的方式然后在最大值上加1来指定主键,在实际项目中不去使用,因为有并发问题
* identity:使用mysql的自增,前提是model类的oid是数值类型,映射column也是数值类型,这种自增没有并发问题,
*因为主键的自增策略由数据库管理(有锁机制)
* sequence:是oracle数据库的自增策略
* Native:是智能的自增策略,会根据数据库的方言来翻译identity或sequence
* UUID:通过hibernate生成一个32位的不重复的字符串,要求是oid是字符串类型,相应的数据库的id也是varchar,
*这种策略在实际项目中大量使用,建议使用UUID,性能高些
*
*环境初始化
* configuration:创建hibernate配置对象,读取hibernate.cfg.xml文件初始化环境
* ServiceRegistry:注册hibernate属性信息
* SessionFactory:Session工程类,这个类是一个重量级别的对象,线程安全的,负责创建session,这个对象
*在服务器启动一次创建一个即可
* session:是一次和数据库的会话,但是不一定是一次连接,session给我们提供很多操作数据库的方法,操作的是对象,
*影响的是数据库
*Transaction:事务对象,控制我们session对象数据库操作事务
*
*hibernate的对象三种状态
* 1.瞬时对象(transientObjects):由我们自己来创建,数据库中没有记录,也没有被hibernate的session管理起来,
*这样的对象可以被jvm回收、
* 2.持久化对象(persistentObjects):这个对象在数据库中有相应的记录,这个对象被session管理起来
* 3.托管对象(detachObjects):数据库中6有相应的记录,但没有被session管理起来
*
*一:
* 瞬时对象--->>持久对象(save()或saveOrUpdate())
* 持久对象--->>瞬时对象(delete())
*
* 持久对象--->>托管对象(evict()或close()或clear())
* 托管对象--->>持久对象(update()或saveOrUpdate())
*
* 托管对象--->>瞬时对象(delete())
*
*二:
* 通过get和load和iterator查询出来的对象是持久状态
*
*设置对象的属性,session会把修改后的持久对象的属性值和没有修改时做对比,如果有变化就发出update,没有就不发
*
*数据库的隔离级别:
* Read uncommitted:不可提交读
* Read commited:提交读
* Repeatable read:重复读
* Serializable read:串行化
*
* 一:脏读:在一个事务之中,数据只做了保存,没有提交的情况下读的数据
* 二:不可重复读:(前后两次读取到的数据不一样)在A事务之中,读取某些数据,
* 另一个B事务对这些数据做修改,A事务提交之前或提交之后,再次读取这些数据,
* 读到的数据是B事务修改后的数据
* 三:可重复读:(前后两次读取到的数据一样)A对某些数据读取时,B对这些数据修改,
* 再提交,但A事务还没有结束,A事务再读取,读取到的是没有修改的数据,
* 前后两次读取到的数据一样,没有体现修改之后数据
* 四:幻读:A事务读取10行数据,B事务插入10行数据,A事务再次读取到20行数据,
* 和第一次读取到的数据不一样
*
*2.session的flush
* 1.使用UUID方式生成主键,由session给生成,把user对象存储在session的临时存储区,在持久区也有一份
* session.save(user);
* 2.flush的时后发出SQL语句,清理临时缓冲区,把dirty变成false,没有提交就可以查询数据
* sessuion.flush();
*
* 1.使用native主键生成策略来测试flush,save的时候要发出SQL来产生主键,持久区有数据,dirty是true
* session.save(user);
* 2.把dirty变成false
* session.flush();
*
* 1.使用native主键生成策略来测试flush,save的时候要发出SQL来产生主键,持久区有数据,dirty是true
* session.save(user);
* 2.把user逐出session
* session.evict(user)
* 3.session中没有数据flush没有意义
* sessiuon.flush();
* 4.但是commit依然会把数据提交到数据库,因为SQL已经发出了
* tx.commit();
*
* name:指定多的一端对中包含一的一端的属性名
* column:多的一端表中对应一的一端的主键作为外键的id(fk)
* <many-to-one name="" column="">
*
* 3.测试多对一:
*1.保存:
* 创建一只球队--创建两个员工--把球队分别加入到两个员工
* 注意在保存多的一端的时候一定要先保存一的一端,如果一的一端是瞬时对象就会报错
* 保存多对一关系的时候也可以使用级联的方式:
* 1.cascade:delete不要用,save-update:级联保存或更新,all包含以上两种,none:不使用级联,默认
* 使用级联的保存就不需要单独保存一的一端了
*
*2.查询:
* 使用get和load来查询:不用开启事务
*一对一关联映射:
* 配置映射文件:
* <hibernate-mapping package="">
* <class name=""table="">
* <id name=""column="">
* 使用外键方式生成主键
* <generator class="foreign">
* 一对一另一端的属性名
* <param name="property"></param>
* </generator>
*` </id>
* <property name="cardNo"column="card_no"></property>
* <one-to-one name="emp"constrained="true"></one-to-one>
</class>
</hibernate-mapping>
*3.一对一单端测试:
* 1.保存:
* 创建员工--创建身份证--指定一对一的关系
* :保存IC的时候自动保存了emp,因为IC的主键是emp的主键
* 2.查询:查询到身份证信息--查询到员工信息
*4.一对一的双向关联映射:
* 修改emp模型,加入IDCard这一端的属性
* 在emp.hbm.xml中配置:<one-to-one name="card"></one-to-one>
* 1.测试保存:
* 创建员工--创建身份证--设置员工和身份证的关系,由于是双向关联映射,需要指定双向的关系
* emp.setCard(ic);
* ic.setEmp(emp);
* 先保存emp:session.save(emp);
* 再保存ic:session.save(ic);
*
*emp端使用级联方式:就可以只保存emp,不用保存IC
* 2.查询:
* 从箭头指向的方向查询会使用连接查询
*
*一对多的关联映射:
* 1.在一的一端指定多的一端的属性:private Set<Emp>set;
* 2.配置映射文件:在一的一端来配置:
* <set name="set">
* <key column="t_id"></key>
* 指定多的一端的类
* <one-to-many class="Emp"/>
* </set>
*3.测试一对多
* 1.保存:
* 创建员工--保存员工--创建球队--把两个员工加入到集合中--把员工加入到球队中--保存球队
* 从输出上可以看出来在保存员工时先插入员工的基本字段,外键t_id是空的,再插入球队,最后根据生成球队的
*t_id来修改两个员工的球队外键t_id,缺点是如果多的一端员工表中外键为非空插入时就会报错。
* 在球队映射文件中加入constrained=cascade级联保存
*
*4.一对多的双向关联
* 一对多是有缺陷,保存的时候如果多的一端的外键是非空,一对多的保存就不成立,所以需要双向关联,在多的一端加入一的一端的属性
* t_id在一的一端已经配置这个属性要一样
* <many-to-one name="team"column="t_id"/>
* 1.保存:
* 创建球队--保存球队--创建员工--把球队加入员工--保存员工
*可以使用控制反转来在一的一端来保存,我们可以在一的一端操作,然后实际是在多的一端保存
* <set name="set" cascade="save-update" inverse="true">
* 创建球队--创建员工--把球队加到员工--保存球队
*
*保存一对多:先保存多的一端Emp,再保存一的一端Team
*一对多双向关联:先保存一的一端Team,再保存多的一端Emp
*
*多对多关联映射:
* 1.指定多对多的单端关系
* 2.单端配置多对多
* 3.<set name="roles"table="emp_roles">:name:在emp中集合属性,table:要产生的中间表名称
* <key column="emp_no"></key>column:当前emp表中主键名称
* <many-to-many class="Role"column="role_id"/>class:指定多的一端的类,column:多的一端给中间表的外键
*
*3.测试单端多对多:
* 1.保存:
* 创建员工--创建2个角色--保存2个角色--把2个角色加入到集合中--把集合中两个角色加入到员工中--保存员工
*
*4.多对多双向关联映射:
* 1.在Role角色中加入Emp的Set集合属性
* 2.修改配置:<set name="emps"table="emp_role"cascade="save-update">table:中间表
* <key column="role_id"/>
* <many-to-many class="Emp"column="emp_no"/>
* </set>
* 和单端的代码基本一致,只是从角色端来看
*
* 13延迟加载
*1.class上的延迟加载:
* 我们通过get和load来查询,get默认不使用延迟加载,load使用延迟加载,延迟加载必须在同一个session范围内
*一:get方法:
* get方法不使用延迟加载,会发出SQL
* User user=(User)session.get(User.class,"297e045d4fd4ddbc014fd4ddbe100000") ;
* System.out.println(user.getUname());
*二:load方法:
* load方法默认使用延迟加载,不发出SQL
* User user=(User)session.load(User.class,"297e045d4fd4ddbc014fd4ddbe100000") ;
* 使用到对象的成员时发出SQL,动态代理
* System.out.println(user.getUname());
*延迟加载必须在同一个session内:
* 类上的延迟加载开关lazy="false";--延迟加载消失
*
* 集合的延迟加载:
* 集合的加载是在一对多和多对多,默认情况集合是有延迟加载的
* 设置级联和控制反转要注意是否要保存,避免破坏了反转
*
* 1.发出查询team的SQL--发出根据t_id查询员工的SQL
* 2.如果set上的lazy=“false”就没有延迟加载效果,在使用team来获得员工才发出SQL,把所有员工查询出来计算数量
* 3.我们把lazy=extra,来发出一条智能高效的SQL,效率比较高
* 4.注意集合上的延迟加载效果不受类上的延迟加载影响
*
*3.单端延迟加载:
* 针对多对一和一对一的延迟加载:默认情况lazy=proxy使用延迟加载
* 1.类的加载不发SQL
* Emp emp=(Emp)session.load(Emp.class,1);
* 2.发出一条根据emp_no查询员工的SQL
* System.out.println(emp);
* 3.不发SQL
* Team team=emp.getTeam();
* 4.发出一条根据t_id查询球队的SQL
* System.out.println(team);
*
*设置lazy=false
* 1.类的加载不发SQL
* Emp emp=(Emp)session.load(Emp.class,1);
* 2.发出一条根据emp_no查询员工的SQL,发出一条根据t_id查询球队的SQL
* System.out.println(emp);
* 3.不发SQL
* Team team=emp.getTeam();
* 4.不发SQL
* System.out.println(team);
*
*14.hibernate查询
* 1、HQL查询:hibernate的专属语言,可以跨数据库,根据不同的数据库方言翻译成不同的SQL,跟SQL很相似
*1.基本查询
* 一:查询所有数据:String hql="from User";
* String hql="select u from User u";//hql中没有*的写法
* 1.查询出所有user类的对象:String hql="from User";
* 2.根据hql语句创建查询对象:Query query=session.createQuery(hql);
* 3.查询列表:List<User>userList=query.list();
* for(User user:userList){
* System.out.println(user);
* }
* 二:对象的单个属性查询:
* String hql="select u.uname from User u";
* 三:多个属性的查询:
* String hql="select u.uname u.gender from User u";
* 四:hibernate的分页查询:
* 1.从前台传递过来的是页码PageNo,startNum=(pageNo-1)*pageSiza
* 2.查询列表
* List<User>userList=query.setFirstResult(5).setMaxResults(5).list();
* for(User user:userList){
* System.out.println(user);
* }
* 查询出5条数据,从userId=6---到---userId=10\
* 五:限定查询:
* 限定查询的第一种方式:
* String hql=“select u from User u where u.gender=? and u.uname=?”;
* Query query=session.createQuery(hql);
* //设置第一个参数的值,和jdbc不同,预编译的索引从0开始
* query.setParameter(0,2);
* query.setParameter(1,"任亮5");
* List<User>userList=query.list();
* for(....){}
* 限定查询第二种方式:
* String hql="select u from User u where u.gender=:gender and u.uname=:uname";
* 六:统计查询:
* String hql="select count(u.userId) from User u";
* max(u.userId),min(u.userId),avg(u.userId),sum(u.userId)
* 七:分组统计查询:
* 统计求和:
* String hql="select avg(u.salary),u.gender from User u group by u.gender having avg(u.salary)>1400";
* 八:投影查询:
* 我们可以创建一个业务bean,在bean提供的有参数的构造器来接收SQL返回值,创建出对象语法
* 在select后面new com.rl.hiber.model.EmpBean(max(u.sal),u.gender)
* String hql="select new com.rl.hiber.model.EmpBean(max(u.salary),u.gender)from User u
* group by u.gender having avg(u.salary)>1400";
*
* 九:排序查询:
* String hql=“select u from User u order by u.salary desc”;
* 十:模糊查询:
* String hql="select u from User u where u.uname like '%亮&'";
*
* 9.HQL提取到配置文件中:
* <query name="getUserAll">
* <![CDATA[from User u where u.salary >:salary]]>
* </query>
* 查询:
* 1.从映射文件中获得hql语句创建query对象
* Query query=session.getNameQuery("getUserAll");
* query.setParameter("salary",1500);
* 2.查询列表
* List <User>userlist=query.list();
* for(User user:userList){
* System.out.println(user);
* }
*
*2.QBC查询:
* QBC是hibernate提供的一套API,跨数据库
* 1.创建去qbc查询接口的实现类
* Criteria c=session.createCriteria(User.class);
* 2.查询User类对象的列表
*
* 单个属性查询:
* 1.创建qbc的查询接口实现类:
* Criteria c=session.createCriteria(User.class);
* 2.获得具体要查询的属性:
* PropertyProjection pp=Projections.property("uname");
* 3.设置具体要查询的列
* c.setProjection(pp);
* 4.查询User类对象的列表
* List<String>uname=c.list();
* 5.循环输出
*
* User对象多个属性的查询:
* 1、创建qbc的查询接口的实现类
* Criteria c=session.createCriteria(User.class);
* 2、创建要查询的列的集合
* ProjectionList p1=Projections.projectionList();
* 3.获得具体要查询的列的属性
* PropertyProjection pp=Projections.property("uname");
* PropertyProjection pp1=Projections.property("gender");
* 4.把要查询的属性加入到集合中
* p1.add(pp);
* p1.add(pp1);
* 5.设置具体要查询的列
* c.setPrtojection(p1);
* 6.查询User类对象的列表
* 7.输出
*
* 2.统计查询:
* 1.创建qbc的查询接口实现类
* Criteria c=session.createCriteria(User.class);
* 2.设置要查询的项
* c.setProijection(Projections.rowCount());
* Object obj=c.uniqueResult();
* System.out.println("总记录数:"+obj);
* 3.设置要查询薪水的最大值
* c.setProjection(projections.max("salary"));
* Object obj=c.uniqueResult();
* System.out.println("最高薪水":"+obj);
* 4.设置要查询薪水的最小值:
* c.setProjection(projections.min("salary"));
* 5.设置要查询薪水的平均值
* c.setProjection(projections.avg("salary"));
* 6.设置要查询薪水的总值
* c.setProjection(projections.sum("salary"));
*
* 3.分组统计查询:
* 1.创建qbc的查询接口实现类
* Criteria c=session.createCriteria(User.class);
* 2.设置要查询的项的集合
* ProjectionList p1=Projections.projectionList();
* 3.创建查询项
* PropertyProjection pp=Projections.property("gender");
* 4.创建统计项
* AggregateProjection ap=Projections.max("salary");
* 5.设置按照那一项来分组
* PropertyProjection pp1=Projections.groupProperty("gender");
* 6.把要查询的项加入到集合
* p1.add(pp);
* p1.add(ap);
* p1.add(pp1);
* 7.把查询集合设置给qbc接口
* c.setProjection(p1);
* 8.查询集合列表
*
* 4.排序:
* 1.创建qbc的查询接口的实现类
* Criteria c=session.createCriteria(User.class);
* 2.设置排序字段
* c.addOrder(Order.desc("salary"));
* 3.查询User类对象的列表
* List<User>userList=c.list();
* 4.遍历集合
* 5.分页查询:
* 1.创建qbc的查询接口实现类
* Criteria c=session.createCtiteria(User.class);
* 2.设置排序字段
* c.addOrder(Order.desc("salary"));
* 3.设置开始行号:
* c.setFirstResults(5);
* 4.设置每页记录数
* c.setMaxResults(5);
* 5.查询User类对象的列表
*
* 6.限定查询:
* 我们通过Restrictions来设置限定查询条件
* Restrictoions.allEq :使用Map,使用key/value进行多个等于的判断
* Restritions.gt :大于
* ge :大于等于
* lt :小于
* le :小于等于
* between :对应sql的between子句
* like :对应sql的like子句
* in :对应SQL的in子句
* and :and关系
* or :or关系
* sqlRestriction :sql限定查询
*
* 继承映射:
* 1.单表继承映射
* <hibernate-mapping package="com.rl.hiber.model">
* <class name="Animal" table="t_animal">
* <id name="anId"column="an_id">
* <generator class="native"/>
* </id>
* <!--鉴别器,在单表中加入一列来区分子类的-->
* <discriminator column="type" type="string"/>
* <property name="anName" column="an_name"></property>
* <property name="gender/">
*
* <!--子类配置-->
* · <subclass name="Pig" discriminator-value="p">
* 子类中属性映射
* <property name="weight"/>
* </subclass>
* <subclass name="Bird" discrimitor-value="b">
* <propertu name="height"/>
* </subclass>
* </hibernate-mapping>
*
* 测试:保存
* 创建猪--保存猪--创建鸟--保存鸟
* 子类查询:
* pig.getAnName()--pig.getWeight();
* 注意:
* load的延迟加载的方式返回的是代理类,所以无法做多态查询
* 但get支持多态查询。
*
* 父子表的映射因为生成的表多张,查询的时候我们需要多表连接查询,所以效率没有单表继承映射高
*
*子表继承映射:
* 1、子表映射,需要把父类的映射设置成抽象的(不会产生父表)
*· <class name="Animal" table="t_animal" abstract="true">
* generator:主键映射策略class="uuid"
* 导出表后,产生两张子表,子表中包含父类的映射字段
*
*16.hibernate的并发控制
* 1、悲观锁:
* 悲观锁不是hibernate的锁,这时数据库的update锁。select * from item for update:
* hibernate就是用这种机制在查询上上锁
* 1.使用悲观锁查询,如果一个请求在查询,另一个请求被阻塞在外
* Item item=(Item)session.load(Item.class,1,LockOptions.UPGRADE);
* item.setStock(item.getStock()-2);
*
* 2.乐观锁:
* 悲观锁的性能比较低,因为查询也被锁住,我们乐观锁可以很好地解决这个问题
* 在配置加version
* Item item=(Item)session.load(Item.class,1)
* item.setStock(item.getStock()-2);
*
* select * from t_item where item_id=1001;
* 1001 iphone 100 1
* Update t_item set t.stock=100-2,t.version=t.version+1 where t.item_id=1001 and t.version=1
*
* 17.抓取策略
* 根据不同的需求来选择不同的策略来查询,后台生成的SQL也不一样
* 1.多对一的单端抓取:many-to-one的fetch=join
*
* 1、Emp emp=(Emp)session.load(Emp.class,1);
* 2.发出一条两张表的连接查询把emp和team的数据都查询出来
* syso(emp);
* Team team=emp.getTeam();
* 3.不发SQL
* syso(team);
*
* 第二种:
* 多对一的单端抓取:many-to-one的fetch=select
*
*`` 1.Emp emp=(Emp)session.load(Emp.class,1);
* 2.发出一条根据emp_no查询emp的SQL
* syso(emp);
*` 3.不发SQL
* Team team=emp.getTeam();
* 4.发出一条根据t_id查询球队的SQL
* syso(team);
*
*
* 抓取策略:获取关联对象
* 二:hibernate抓取策略分类:
* hibernate有如下四种原生态的Hibernate抓取策略,分别是select fetching
* join fetching,subselect fetching,Batch fetching.
* 1.连接抓取(Join fetching):Hibernate通过SELECT语句使用OUTER JOIN(外连接)来获得对象的关联实例或者关联集合。
* 配置:<many-to-one name="classes"column="classesid" fetch="join"/>
* 执行结果:fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合
* 此时lazy会失效
* 如果在配置中添加一个非空属性,连接就成内连接了:
* <many-to-one name="classes" column="classesid" fetch="join" not-null="true"/>
* 查询结果:带有inner join t_classes
* 2.查询抓取(Select fetching):另外发出一条SELECT语句抓取当前对象关联的实体或集合,除非你显示地指定
* lazy="false",否则只有访问到关联关系时才会执行第二条select语句
* 配置:<many-to-one name="classes" column="classesid" fetch="select"/>
* 执行结果:两条查询语句
* 3.子查询抓取(Subselect fetching):另外发送一条select语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合
* 4.批量抓取(batch fetching):对查询抓取的优化方案,通过指定一个主键或外键列表,Hibernate使用一条select语句
* 获取一批对象的实例集合
* 配置:<class name="com....Classes"table="t_classes"batch-size="10">
* 使用batch也可以解决N+1问题
* 在集合上使用:batch-size属性还可以批量加载实体类<set name="students" inverse="true" cascade="all" batch-size="5">
* 当student对象时发出10条hql语句配置过后batch-size=5后会发10/5=2条hql语句,提高性能
*
*缓冲:
* 1、一级缓存:
* 一级缓存也叫session的缓存,不能跨session,如果session关闭那么缓冲数据也清理掉
* 一级缓存只能缓存实体对象,不能缓存属性
* GET load iterate使用一级缓存,list不能使用一级缓存
* 缓存的刷新,数据库的数据被修改的同时,缓冲的数据也会被同步
* 一级缓存的管理使用session的evict和clear方法
* 2.二级缓存
* 1.二级缓存是SessionFactory级别的缓存,这个缓存能被所有的session共享,可以跨session,
* 如果SessionFactory关闭,二级缓存随之消失
* 2.二级缓存也是缓存实体对象。不能对普通属性做缓存
* 3.二级缓存的应用场景
* a.适合使用二级缓存:
* 1.数据很少被修改
* 2.数据不是特别重要,运行偶尔的并发
* 3.参考数据
* b.不适合使用二级缓存:
* 1.经常要修改的数据
* 2.财务数据,绝对不允许并发问题出现
* 3.和其他环境的数据做贡献数据
*
* 3.EHcatch
* 支持在内存和硬盘上做缓存,能支持集群,支持查询缓存
* Ehcache配置:
* maxElementslnMemory:缓存中对象的个数
* 。。。
* 使用二级缓存的方法:get load iterate 不使用list
*
* 4.二级缓存策略
* 二级缓存的策略由<cache>中的usage来控制
* Read-only:只读缓存,二级缓存中的数据无法修改
* Nonstrict-read-write:允许更新数据库,一旦更新数据库的数据相应的二级缓存中的数据就过期了
* 就会被清理掉,再次做同样查询就会发出SQL。允许新增,不会清掉二级缓存,而是在二级缓存中加入数据
* Read-write:允许更新数据库,一旦更新数据库,那么缓冲也会被同步更新
*
* 5.手动管理二级缓存:
* 通过SessionFactory来获得二级缓存对象,来管理
*/
*
* 1.Transient Objects:瞬时对象
* 2.Persistent Objects:持久对象
* 3.session:hibernate的会话
* 4.SessionFactory:会话工厂,进程级别的对象,重量级的
* 5.Transaction:本地事务
* 6.TransactionFactory:事务工厂
* 7.ConnectionProvider:使用第三方数据源,如c3p0,dbcp
* 8.JNDI:java name Directory interface:提供资源目录
* 9.JDBC:操作数据库的api
* 10.JTA:跨数据库的事务
*
* 1.1.3每张table表的映射文件:Event.hbm.xml
* 映射文件--告诉hibernate--访问数据库里面的那个表以及应该使用表里的那些字段
* <hibernate-mapping package="">
* <class name="Event" table="EVENTS">
* <id name="id" column="EVENT_ID">
* <generation class="native"/>
* </id>
* <property name="date" type="timestamp" column="EVENT_DATE"/>
* <property name="title"/>
* </class>
*</hibernate-mapping>
*1.timestape列用于insert或update操作时记录日期和时间
*2.如果你不分配一个值,表中的第一个timestamp列自动设置为最近操作的日期和时间
*3.也可以通过分配一个null值,将timestamp列设置为当前的日期和时间
*4.timestamp值返回后显示为YYYY-MM-DD HH:MM:SS格式的字符串
*5.package:指定当前映射文件的实体类model所在的包
*
* 1.1.4hibernate的核心配置文件:hibernate.cfg.xml
* <hibernate-configuration>
* <session-factory>
* <property name=""></property>
* ................
* 数据库连接设置
* 可选配置
* 设置数据库连接池的初始化连接数
* <mapping resource="com/rl/hiber/model/Event.hbm.xml"/>
* </session-Factory>
* </hibernate-configuration>
*
* 1.如果使用对个数据库,就要用多个<session-factory>,通常放在多个配置文件中,更容易启动
*
* 生成表::
* 1.创建hibernate的配置对象
* Configuration cfg=new Configuration();
* 2.指定hibernate核心配置文件:hibernate.cfg.xml的位置
* cfg.configura("hibernate.cfg.xml");
* 3.创建表的对象
* SchemaExport se=new SchemaExport(cfg);
* se.create(true,true);
*
* 测试--插入数据
*1.创建hibernate的配置对象
*Configuration cfg=new Configuration();
*2.指定hibernate核心配置文件的位置
*cfg.configure("hibernate,cfg.xml");
*3.注册配置属性信息
*ServiceRegistry sr=new StandardServiceRegistryBuilder()
* .applySettings(cfg.getPropertirs()).build();
*4.创建SessionFactory
*SessionFactory factory=cfg.buildSessionFactory(sr);
*5.创建session
*Session session=factory.openSession();
*6.开启事务
*Transaction tx=session.beginTransaction();
*7.创建对象User,并给对象赋值
*8.通过session保存对象
*session.save(user);
*9.提交事务
*tx.commit();
*10.关闭session
*session.close();
*
*模型类的规范:
*1.必须要有默认构造方法:查询时把数据表中的一条数据映射成一个对象时需要使用默认的构造器来创建对象
*2.必须提供一个OID,作为对象的主键
*3.属性的私有化封装,提供set和get方法
*4.不要使用final来修饰模型类中的成员,如果是final类型后续延迟加载无法实现
*5.最好使用包装类
*
*属性映射
*property:属性映射
*属性:
* name:模型类的属性名
* column:映射到表中的列名
* type:hibernate提供的数据类型,可以由java的数据类型转换成数据库中的数据类型
* length:映射到数据库中字段的长度
* not-null:是否可以为空
* unique-key:唯一约束的键名
*主键映射策略:
* assigned:手动指定id,在实际项目中基本不用
* increment:通过获得当前的id的最大值的方式然后在最大值上加1来指定主键,在实际项目中不去使用,因为有并发问题
* identity:使用mysql的自增,前提是model类的oid是数值类型,映射column也是数值类型,这种自增没有并发问题,
*因为主键的自增策略由数据库管理(有锁机制)
* sequence:是oracle数据库的自增策略
* Native:是智能的自增策略,会根据数据库的方言来翻译identity或sequence
* UUID:通过hibernate生成一个32位的不重复的字符串,要求是oid是字符串类型,相应的数据库的id也是varchar,
*这种策略在实际项目中大量使用,建议使用UUID,性能高些
*
*环境初始化
* configuration:创建hibernate配置对象,读取hibernate.cfg.xml文件初始化环境
* ServiceRegistry:注册hibernate属性信息
* SessionFactory:Session工程类,这个类是一个重量级别的对象,线程安全的,负责创建session,这个对象
*在服务器启动一次创建一个即可
* session:是一次和数据库的会话,但是不一定是一次连接,session给我们提供很多操作数据库的方法,操作的是对象,
*影响的是数据库
*Transaction:事务对象,控制我们session对象数据库操作事务
*
*hibernate的对象三种状态
* 1.瞬时对象(transientObjects):由我们自己来创建,数据库中没有记录,也没有被hibernate的session管理起来,
*这样的对象可以被jvm回收、
* 2.持久化对象(persistentObjects):这个对象在数据库中有相应的记录,这个对象被session管理起来
* 3.托管对象(detachObjects):数据库中6有相应的记录,但没有被session管理起来
*
*一:
* 瞬时对象--->>持久对象(save()或saveOrUpdate())
* 持久对象--->>瞬时对象(delete())
*
* 持久对象--->>托管对象(evict()或close()或clear())
* 托管对象--->>持久对象(update()或saveOrUpdate())
*
* 托管对象--->>瞬时对象(delete())
*
*二:
* 通过get和load和iterator查询出来的对象是持久状态
*
*设置对象的属性,session会把修改后的持久对象的属性值和没有修改时做对比,如果有变化就发出update,没有就不发
*
*数据库的隔离级别:
* Read uncommitted:不可提交读
* Read commited:提交读
* Repeatable read:重复读
* Serializable read:串行化
*
* 一:脏读:在一个事务之中,数据只做了保存,没有提交的情况下读的数据
* 二:不可重复读:(前后两次读取到的数据不一样)在A事务之中,读取某些数据,
* 另一个B事务对这些数据做修改,A事务提交之前或提交之后,再次读取这些数据,
* 读到的数据是B事务修改后的数据
* 三:可重复读:(前后两次读取到的数据一样)A对某些数据读取时,B对这些数据修改,
* 再提交,但A事务还没有结束,A事务再读取,读取到的是没有修改的数据,
* 前后两次读取到的数据一样,没有体现修改之后数据
* 四:幻读:A事务读取10行数据,B事务插入10行数据,A事务再次读取到20行数据,
* 和第一次读取到的数据不一样
*
*2.session的flush
* 1.使用UUID方式生成主键,由session给生成,把user对象存储在session的临时存储区,在持久区也有一份
* session.save(user);
* 2.flush的时后发出SQL语句,清理临时缓冲区,把dirty变成false,没有提交就可以查询数据
* sessuion.flush();
*
* 1.使用native主键生成策略来测试flush,save的时候要发出SQL来产生主键,持久区有数据,dirty是true
* session.save(user);
* 2.把dirty变成false
* session.flush();
*
* 1.使用native主键生成策略来测试flush,save的时候要发出SQL来产生主键,持久区有数据,dirty是true
* session.save(user);
* 2.把user逐出session
* session.evict(user)
* 3.session中没有数据flush没有意义
* sessiuon.flush();
* 4.但是commit依然会把数据提交到数据库,因为SQL已经发出了
* tx.commit();
*
* name:指定多的一端对中包含一的一端的属性名
* column:多的一端表中对应一的一端的主键作为外键的id(fk)
* <many-to-one name="" column="">
*
* 3.测试多对一:
*1.保存:
* 创建一只球队--创建两个员工--把球队分别加入到两个员工
* 注意在保存多的一端的时候一定要先保存一的一端,如果一的一端是瞬时对象就会报错
* 保存多对一关系的时候也可以使用级联的方式:
* 1.cascade:delete不要用,save-update:级联保存或更新,all包含以上两种,none:不使用级联,默认
* 使用级联的保存就不需要单独保存一的一端了
*
*2.查询:
* 使用get和load来查询:不用开启事务
*一对一关联映射:
* 配置映射文件:
* <hibernate-mapping package="">
* <class name=""table="">
* <id name=""column="">
* 使用外键方式生成主键
* <generator class="foreign">
* 一对一另一端的属性名
* <param name="property"></param>
* </generator>
*` </id>
* <property name="cardNo"column="card_no"></property>
* <one-to-one name="emp"constrained="true"></one-to-one>
</class>
</hibernate-mapping>
*3.一对一单端测试:
* 1.保存:
* 创建员工--创建身份证--指定一对一的关系
* :保存IC的时候自动保存了emp,因为IC的主键是emp的主键
* 2.查询:查询到身份证信息--查询到员工信息
*4.一对一的双向关联映射:
* 修改emp模型,加入IDCard这一端的属性
* 在emp.hbm.xml中配置:<one-to-one name="card"></one-to-one>
* 1.测试保存:
* 创建员工--创建身份证--设置员工和身份证的关系,由于是双向关联映射,需要指定双向的关系
* emp.setCard(ic);
* ic.setEmp(emp);
* 先保存emp:session.save(emp);
* 再保存ic:session.save(ic);
*
*emp端使用级联方式:就可以只保存emp,不用保存IC
* 2.查询:
* 从箭头指向的方向查询会使用连接查询
*
*一对多的关联映射:
* 1.在一的一端指定多的一端的属性:private Set<Emp>set;
* 2.配置映射文件:在一的一端来配置:
* <set name="set">
* <key column="t_id"></key>
* 指定多的一端的类
* <one-to-many class="Emp"/>
* </set>
*3.测试一对多
* 1.保存:
* 创建员工--保存员工--创建球队--把两个员工加入到集合中--把员工加入到球队中--保存球队
* 从输出上可以看出来在保存员工时先插入员工的基本字段,外键t_id是空的,再插入球队,最后根据生成球队的
*t_id来修改两个员工的球队外键t_id,缺点是如果多的一端员工表中外键为非空插入时就会报错。
* 在球队映射文件中加入constrained=cascade级联保存
*
*4.一对多的双向关联
* 一对多是有缺陷,保存的时候如果多的一端的外键是非空,一对多的保存就不成立,所以需要双向关联,在多的一端加入一的一端的属性
* t_id在一的一端已经配置这个属性要一样
* <many-to-one name="team"column="t_id"/>
* 1.保存:
* 创建球队--保存球队--创建员工--把球队加入员工--保存员工
*可以使用控制反转来在一的一端来保存,我们可以在一的一端操作,然后实际是在多的一端保存
* <set name="set" cascade="save-update" inverse="true">
* 创建球队--创建员工--把球队加到员工--保存球队
*
*保存一对多:先保存多的一端Emp,再保存一的一端Team
*一对多双向关联:先保存一的一端Team,再保存多的一端Emp
*
*多对多关联映射:
* 1.指定多对多的单端关系
* 2.单端配置多对多
* 3.<set name="roles"table="emp_roles">:name:在emp中集合属性,table:要产生的中间表名称
* <key column="emp_no"></key>column:当前emp表中主键名称
* <many-to-many class="Role"column="role_id"/>class:指定多的一端的类,column:多的一端给中间表的外键
*
*3.测试单端多对多:
* 1.保存:
* 创建员工--创建2个角色--保存2个角色--把2个角色加入到集合中--把集合中两个角色加入到员工中--保存员工
*
*4.多对多双向关联映射:
* 1.在Role角色中加入Emp的Set集合属性
* 2.修改配置:<set name="emps"table="emp_role"cascade="save-update">table:中间表
* <key column="role_id"/>
* <many-to-many class="Emp"column="emp_no"/>
* </set>
* 和单端的代码基本一致,只是从角色端来看
*
* 13延迟加载
*1.class上的延迟加载:
* 我们通过get和load来查询,get默认不使用延迟加载,load使用延迟加载,延迟加载必须在同一个session范围内
*一:get方法:
* get方法不使用延迟加载,会发出SQL
* User user=(User)session.get(User.class,"297e045d4fd4ddbc014fd4ddbe100000") ;
* System.out.println(user.getUname());
*二:load方法:
* load方法默认使用延迟加载,不发出SQL
* User user=(User)session.load(User.class,"297e045d4fd4ddbc014fd4ddbe100000") ;
* 使用到对象的成员时发出SQL,动态代理
* System.out.println(user.getUname());
*延迟加载必须在同一个session内:
* 类上的延迟加载开关lazy="false";--延迟加载消失
*
* 集合的延迟加载:
* 集合的加载是在一对多和多对多,默认情况集合是有延迟加载的
* 设置级联和控制反转要注意是否要保存,避免破坏了反转
*
* 1.发出查询team的SQL--发出根据t_id查询员工的SQL
* 2.如果set上的lazy=“false”就没有延迟加载效果,在使用team来获得员工才发出SQL,把所有员工查询出来计算数量
* 3.我们把lazy=extra,来发出一条智能高效的SQL,效率比较高
* 4.注意集合上的延迟加载效果不受类上的延迟加载影响
*
*3.单端延迟加载:
* 针对多对一和一对一的延迟加载:默认情况lazy=proxy使用延迟加载
* 1.类的加载不发SQL
* Emp emp=(Emp)session.load(Emp.class,1);
* 2.发出一条根据emp_no查询员工的SQL
* System.out.println(emp);
* 3.不发SQL
* Team team=emp.getTeam();
* 4.发出一条根据t_id查询球队的SQL
* System.out.println(team);
*
*设置lazy=false
* 1.类的加载不发SQL
* Emp emp=(Emp)session.load(Emp.class,1);
* 2.发出一条根据emp_no查询员工的SQL,发出一条根据t_id查询球队的SQL
* System.out.println(emp);
* 3.不发SQL
* Team team=emp.getTeam();
* 4.不发SQL
* System.out.println(team);
*
*14.hibernate查询
* 1、HQL查询:hibernate的专属语言,可以跨数据库,根据不同的数据库方言翻译成不同的SQL,跟SQL很相似
*1.基本查询
* 一:查询所有数据:String hql="from User";
* String hql="select u from User u";//hql中没有*的写法
* 1.查询出所有user类的对象:String hql="from User";
* 2.根据hql语句创建查询对象:Query query=session.createQuery(hql);
* 3.查询列表:List<User>userList=query.list();
* for(User user:userList){
* System.out.println(user);
* }
* 二:对象的单个属性查询:
* String hql="select u.uname from User u";
* 三:多个属性的查询:
* String hql="select u.uname u.gender from User u";
* 四:hibernate的分页查询:
* 1.从前台传递过来的是页码PageNo,startNum=(pageNo-1)*pageSiza
* 2.查询列表
* List<User>userList=query.setFirstResult(5).setMaxResults(5).list();
* for(User user:userList){
* System.out.println(user);
* }
* 查询出5条数据,从userId=6---到---userId=10\
* 五:限定查询:
* 限定查询的第一种方式:
* String hql=“select u from User u where u.gender=? and u.uname=?”;
* Query query=session.createQuery(hql);
* //设置第一个参数的值,和jdbc不同,预编译的索引从0开始
* query.setParameter(0,2);
* query.setParameter(1,"任亮5");
* List<User>userList=query.list();
* for(....){}
* 限定查询第二种方式:
* String hql="select u from User u where u.gender=:gender and u.uname=:uname";
* 六:统计查询:
* String hql="select count(u.userId) from User u";
* max(u.userId),min(u.userId),avg(u.userId),sum(u.userId)
* 七:分组统计查询:
* 统计求和:
* String hql="select avg(u.salary),u.gender from User u group by u.gender having avg(u.salary)>1400";
* 八:投影查询:
* 我们可以创建一个业务bean,在bean提供的有参数的构造器来接收SQL返回值,创建出对象语法
* 在select后面new com.rl.hiber.model.EmpBean(max(u.sal),u.gender)
* String hql="select new com.rl.hiber.model.EmpBean(max(u.salary),u.gender)from User u
* group by u.gender having avg(u.salary)>1400";
*
* 九:排序查询:
* String hql=“select u from User u order by u.salary desc”;
* 十:模糊查询:
* String hql="select u from User u where u.uname like '%亮&'";
*
* 9.HQL提取到配置文件中:
* <query name="getUserAll">
* <![CDATA[from User u where u.salary >:salary]]>
* </query>
* 查询:
* 1.从映射文件中获得hql语句创建query对象
* Query query=session.getNameQuery("getUserAll");
* query.setParameter("salary",1500);
* 2.查询列表
* List <User>userlist=query.list();
* for(User user:userList){
* System.out.println(user);
* }
*
*2.QBC查询:
* QBC是hibernate提供的一套API,跨数据库
* 1.创建去qbc查询接口的实现类
* Criteria c=session.createCriteria(User.class);
* 2.查询User类对象的列表
*
* 单个属性查询:
* 1.创建qbc的查询接口实现类:
* Criteria c=session.createCriteria(User.class);
* 2.获得具体要查询的属性:
* PropertyProjection pp=Projections.property("uname");
* 3.设置具体要查询的列
* c.setProjection(pp);
* 4.查询User类对象的列表
* List<String>uname=c.list();
* 5.循环输出
*
* User对象多个属性的查询:
* 1、创建qbc的查询接口的实现类
* Criteria c=session.createCriteria(User.class);
* 2、创建要查询的列的集合
* ProjectionList p1=Projections.projectionList();
* 3.获得具体要查询的列的属性
* PropertyProjection pp=Projections.property("uname");
* PropertyProjection pp1=Projections.property("gender");
* 4.把要查询的属性加入到集合中
* p1.add(pp);
* p1.add(pp1);
* 5.设置具体要查询的列
* c.setPrtojection(p1);
* 6.查询User类对象的列表
* 7.输出
*
* 2.统计查询:
* 1.创建qbc的查询接口实现类
* Criteria c=session.createCriteria(User.class);
* 2.设置要查询的项
* c.setProijection(Projections.rowCount());
* Object obj=c.uniqueResult();
* System.out.println("总记录数:"+obj);
* 3.设置要查询薪水的最大值
* c.setProjection(projections.max("salary"));
* Object obj=c.uniqueResult();
* System.out.println("最高薪水":"+obj);
* 4.设置要查询薪水的最小值:
* c.setProjection(projections.min("salary"));
* 5.设置要查询薪水的平均值
* c.setProjection(projections.avg("salary"));
* 6.设置要查询薪水的总值
* c.setProjection(projections.sum("salary"));
*
* 3.分组统计查询:
* 1.创建qbc的查询接口实现类
* Criteria c=session.createCriteria(User.class);
* 2.设置要查询的项的集合
* ProjectionList p1=Projections.projectionList();
* 3.创建查询项
* PropertyProjection pp=Projections.property("gender");
* 4.创建统计项
* AggregateProjection ap=Projections.max("salary");
* 5.设置按照那一项来分组
* PropertyProjection pp1=Projections.groupProperty("gender");
* 6.把要查询的项加入到集合
* p1.add(pp);
* p1.add(ap);
* p1.add(pp1);
* 7.把查询集合设置给qbc接口
* c.setProjection(p1);
* 8.查询集合列表
*
* 4.排序:
* 1.创建qbc的查询接口的实现类
* Criteria c=session.createCriteria(User.class);
* 2.设置排序字段
* c.addOrder(Order.desc("salary"));
* 3.查询User类对象的列表
* List<User>userList=c.list();
* 4.遍历集合
* 5.分页查询:
* 1.创建qbc的查询接口实现类
* Criteria c=session.createCtiteria(User.class);
* 2.设置排序字段
* c.addOrder(Order.desc("salary"));
* 3.设置开始行号:
* c.setFirstResults(5);
* 4.设置每页记录数
* c.setMaxResults(5);
* 5.查询User类对象的列表
*
* 6.限定查询:
* 我们通过Restrictions来设置限定查询条件
* Restrictoions.allEq :使用Map,使用key/value进行多个等于的判断
* Restritions.gt :大于
* ge :大于等于
* lt :小于
* le :小于等于
* between :对应sql的between子句
* like :对应sql的like子句
* in :对应SQL的in子句
* and :and关系
* or :or关系
* sqlRestriction :sql限定查询
*
* 继承映射:
* 1.单表继承映射
* <hibernate-mapping package="com.rl.hiber.model">
* <class name="Animal" table="t_animal">
* <id name="anId"column="an_id">
* <generator class="native"/>
* </id>
* <!--鉴别器,在单表中加入一列来区分子类的-->
* <discriminator column="type" type="string"/>
* <property name="anName" column="an_name"></property>
* <property name="gender/">
*
* <!--子类配置-->
* · <subclass name="Pig" discriminator-value="p">
* 子类中属性映射
* <property name="weight"/>
* </subclass>
* <subclass name="Bird" discrimitor-value="b">
* <propertu name="height"/>
* </subclass>
* </hibernate-mapping>
*
* 测试:保存
* 创建猪--保存猪--创建鸟--保存鸟
* 子类查询:
* pig.getAnName()--pig.getWeight();
* 注意:
* load的延迟加载的方式返回的是代理类,所以无法做多态查询
* 但get支持多态查询。
*
* 父子表的映射因为生成的表多张,查询的时候我们需要多表连接查询,所以效率没有单表继承映射高
*
*子表继承映射:
* 1、子表映射,需要把父类的映射设置成抽象的(不会产生父表)
*· <class name="Animal" table="t_animal" abstract="true">
* generator:主键映射策略class="uuid"
* 导出表后,产生两张子表,子表中包含父类的映射字段
*
*16.hibernate的并发控制
* 1、悲观锁:
* 悲观锁不是hibernate的锁,这时数据库的update锁。select * from item for update:
* hibernate就是用这种机制在查询上上锁
* 1.使用悲观锁查询,如果一个请求在查询,另一个请求被阻塞在外
* Item item=(Item)session.load(Item.class,1,LockOptions.UPGRADE);
* item.setStock(item.getStock()-2);
*
* 2.乐观锁:
* 悲观锁的性能比较低,因为查询也被锁住,我们乐观锁可以很好地解决这个问题
* 在配置加version
* Item item=(Item)session.load(Item.class,1)
* item.setStock(item.getStock()-2);
*
* select * from t_item where item_id=1001;
* 1001 iphone 100 1
* Update t_item set t.stock=100-2,t.version=t.version+1 where t.item_id=1001 and t.version=1
*
* 17.抓取策略
* 根据不同的需求来选择不同的策略来查询,后台生成的SQL也不一样
* 1.多对一的单端抓取:many-to-one的fetch=join
*
* 1、Emp emp=(Emp)session.load(Emp.class,1);
* 2.发出一条两张表的连接查询把emp和team的数据都查询出来
* syso(emp);
* Team team=emp.getTeam();
* 3.不发SQL
* syso(team);
*
* 第二种:
* 多对一的单端抓取:many-to-one的fetch=select
*
*`` 1.Emp emp=(Emp)session.load(Emp.class,1);
* 2.发出一条根据emp_no查询emp的SQL
* syso(emp);
*` 3.不发SQL
* Team team=emp.getTeam();
* 4.发出一条根据t_id查询球队的SQL
* syso(team);
*
*
* 抓取策略:获取关联对象
* 二:hibernate抓取策略分类:
* hibernate有如下四种原生态的Hibernate抓取策略,分别是select fetching
* join fetching,subselect fetching,Batch fetching.
* 1.连接抓取(Join fetching):Hibernate通过SELECT语句使用OUTER JOIN(外连接)来获得对象的关联实例或者关联集合。
* 配置:<many-to-one name="classes"column="classesid" fetch="join"/>
* 执行结果:fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合
* 此时lazy会失效
* 如果在配置中添加一个非空属性,连接就成内连接了:
* <many-to-one name="classes" column="classesid" fetch="join" not-null="true"/>
* 查询结果:带有inner join t_classes
* 2.查询抓取(Select fetching):另外发出一条SELECT语句抓取当前对象关联的实体或集合,除非你显示地指定
* lazy="false",否则只有访问到关联关系时才会执行第二条select语句
* 配置:<many-to-one name="classes" column="classesid" fetch="select"/>
* 执行结果:两条查询语句
* 3.子查询抓取(Subselect fetching):另外发送一条select语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合
* 4.批量抓取(batch fetching):对查询抓取的优化方案,通过指定一个主键或外键列表,Hibernate使用一条select语句
* 获取一批对象的实例集合
* 配置:<class name="com....Classes"table="t_classes"batch-size="10">
* 使用batch也可以解决N+1问题
* 在集合上使用:batch-size属性还可以批量加载实体类<set name="students" inverse="true" cascade="all" batch-size="5">
* 当student对象时发出10条hql语句配置过后batch-size=5后会发10/5=2条hql语句,提高性能
*
*缓冲:
* 1、一级缓存:
* 一级缓存也叫session的缓存,不能跨session,如果session关闭那么缓冲数据也清理掉
* 一级缓存只能缓存实体对象,不能缓存属性
* GET load iterate使用一级缓存,list不能使用一级缓存
* 缓存的刷新,数据库的数据被修改的同时,缓冲的数据也会被同步
* 一级缓存的管理使用session的evict和clear方法
* 2.二级缓存
* 1.二级缓存是SessionFactory级别的缓存,这个缓存能被所有的session共享,可以跨session,
* 如果SessionFactory关闭,二级缓存随之消失
* 2.二级缓存也是缓存实体对象。不能对普通属性做缓存
* 3.二级缓存的应用场景
* a.适合使用二级缓存:
* 1.数据很少被修改
* 2.数据不是特别重要,运行偶尔的并发
* 3.参考数据
* b.不适合使用二级缓存:
* 1.经常要修改的数据
* 2.财务数据,绝对不允许并发问题出现
* 3.和其他环境的数据做贡献数据
*
* 3.EHcatch
* 支持在内存和硬盘上做缓存,能支持集群,支持查询缓存
* Ehcache配置:
* maxElementslnMemory:缓存中对象的个数
* 。。。
* 使用二级缓存的方法:get load iterate 不使用list
*
* 4.二级缓存策略
* 二级缓存的策略由<cache>中的usage来控制
* Read-only:只读缓存,二级缓存中的数据无法修改
* Nonstrict-read-write:允许更新数据库,一旦更新数据库的数据相应的二级缓存中的数据就过期了
* 就会被清理掉,再次做同样查询就会发出SQL。允许新增,不会清掉二级缓存,而是在二级缓存中加入数据
* Read-write:允许更新数据库,一旦更新数据库,那么缓冲也会被同步更新
*
* 5.手动管理二级缓存:
* 通过SessionFactory来获得二级缓存对象,来管理
*/