hibernate(jpa)+spring

来源:互联网 发布:mac右键没有剪切 编辑:程序博客网 时间:2024/06/07 03:42

项目中用到了hibernate,抽个时间记录下(前面有个简陋的介绍spring+redis+hibernate,此篇对hibernate有个更为详细的使用).

Hibernate是一种ORM(Object Relational Mapping)映射工具,能够建立面向对象的域模型和关系数据模型之间的映射

Object Mapping(Hibernate) Relational

面向对象领域 关系数据库领域

类:Class 表:Table

对象:内存中 表:记录

Java类型 字段:类型

  Hibernate通过操作实体对象来操作数据库。

hibernate是一种持久层的技术,而相关持久层技术

项目中mybatis与hibernate都有接触,当有一套成型的hibernate Dao,开发相对于mybatis要快速很多.mybatis与hibernate各有优劣,但个人感觉hibernate更贴切与对象这个概念,尤其是实现了JPA之后,实现各个对象之间的关联关系显得更加方便,然而对于一个摸索中的项目,往往一对多,多对多等关联关系上显得十分慎用,业务越复杂的时候,关联关系越复杂,级联级别越多,一个错误的删除往往会引发一系列的连锁反应,况且,业务中假删除应用的更多,而hibernate中没有直接支持的假删除.

偷了个懒,没有用jdk1.8版本测试,spring4.3.4+hibernate5.1.3+jdk1.7+mysql5.相对于hibernate臃肿的xml文件配置,个人更倾向于使用JPA注解开发(模板用习惯了,这里仅仅就用了JPA orm部分,对象的相关操作使用hibernateTemplate,而并没有使用JPA的entitymanager)

pom.xml

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><hibernate.version>5.1.3.Final</hibernate.version><spring.version>4.3.4.RELEASE</spring.version></properties><dependencies><!--spring整合redis,模板.工厂.连接池等 --><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>1.8.0.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--spring测试 --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version></dependency><!--redis客户端,连接redis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version></dependency><!--数据源 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.18</version></dependency><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><!--hibernate所需依赖 --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>${hibernate.version}</version></dependency><!-- for JPA, use hibernate-entitymanager instead of hibernate-core --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-entitymanager</artifactId><version>${hibernate.version}</version></dependency><!--使用hibernate jpa 依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>${spring.version}</version></dependency><!--hibernate log4j依赖--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.6.2</version></dependency></dependencies>

spring-hiberlate.xml

<?xml version="1.0" encoding="UTF-8"?><!--ignore-unresolvable=“true" 设置true找不到不会报错,默认false --><!-- <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="false"/> --><!-- 下面的变量从配置文件里面拿出来 --><bean id="propertyPlaceholderConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="locations"><list><value>classpath:jdbc.properties</value></list></property></bean><!-- 数据源 --><bean id="C3P0DataSourceFather" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close" abstract="true"><!--设置数据库连接 --><property name="driverClass" value="com.mysql.jdbc.Driver" /><property name="autoCommitOnClose" value="false" /><!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 --><property name="acquireIncrement" value="2" /><!-- 检测pool内的连接是否正常,此参数就是Task运行的频率 --><property name="idleConnectionTestPeriod" value="60" /><!-- true表示每次把连接checkin到pool里的时候测试其有效性,异步操作,会造成至少多一倍的数据库调用 --><property name="testConnectionOnCheckin" value="false" /><!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 --><property name="acquireRetryAttempts" value="600" /><!-- 两次连接中间隔时间,单位毫秒。Default: 1000 --><property name="acquireRetryDelay" value="1000" /><!-- 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。Default:0 --><property name="checkoutTimeout" value="10000" /><property name="maxStatements" value="0" /><!-- 初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。 Default: 3 --><property name="initialPoolSize" value="20" /><!-- 最小连接数 --><property name="minPoolSize" value="3" /><!-- 连接池中保留的最大连接数。Default: 15 --><property name="maxPoolSize" value="20" /><!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 --><property name="maxIdleTime" value="60" /><!-- How long to hang on to excess unused connections after traffic spike --><property name="maxIdleTimeExcessConnections" value="600" /></bean><!-- 主数据库 --><bean id="dataSource" parent="C3P0DataSourceFather"><property name="jdbcUrl" value="${db.jdbcUrl}" /><property name="user" value="${db.user}" /><property name="password" value="${db.password}" /></bean><!-- 主数据库 sessionFactory --><bean id="sessionFactory"class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="packagesToScan"><list><!--model,对象与数据库的之间的印射关系 --><value>spring.hibernate.model</value></list></property><property name="hibernateProperties"><props><!--数据库方言,对于不同数据库选用不同的方言(mysql切换oracle十分方便) --><prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop><!--开发调试使用 --><!--控制台打印sql语句 --><prop key="hibernate.show_sql">true</prop><!--格式化语句 --><prop key="hibernate.format_sql">true</prop><!--如果开启, Hibernate将在SQL中生成有助于调试的注释信息, 默认值为false --><prop key="hibernate.use_sql_commants">false</prop><!--自动建表策略,create,update,create-drop,validate,运行时期此属性不建议设置 --><!-- <prop key="hibernate.hbm2ddl.auto">update</prop> --><!--批量处理 --><!--Hibernate每次从数据库中取出并放到JDBC的Statement中的记录条数。Fetch Size设的越大,读数据库的次数越少,速度越快,Fetch Size越小,读数据库的次数越多,速度越慢 --><prop key="hibernate.jdbc.fetch_size">50</prop><!--Hibernate批量插入,删除和更新时每次操作的记录数。Batch Size越大,批量操作的向数据库发送Sql的次数越少,速度就越快,同样耗用内存就越大 --><prop key="hibernate.jdbc.batch_size">50</prop><!--是否允许Hibernate用JDBC的可滚动的结果集。对分页的结果集。对分页时的设置非常有帮助 --><prop key="hibernate.jdbc.use_scrollable_resultset">false</prop></props></property></bean><!--开启注解 --><tx:annotation-driven transaction-manager="transactionManager" /> <!--扫描 --><context:component-scan base-package="spring" /> <!--事务 --><bean id="transactionManager"class="org.springframework.orm.hibernate5.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory" /></bean><!--hibernate模板 --><bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate"><property name="sessionFactory" ref="sessionFactory" /></bean>

hibernate相关参数配置(hibernate参数配置)

定义一些model

baseEntity

package spring.hibernate.model;import java.util.Date;import javax.persistence.Column;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.MappedSuperclass;import javax.persistence.Temporal;import javax.persistence.TemporalType;@MappedSuperclasspublic class BaseEntity {/** id */protected Integer id;/** 创建日期 */protected Date createTime = new Date();@Id@GeneratedValue(strategy = GenerationType.IDENTITY)public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}@Column(name = "create_time", nullable = false, updatable = false)@Temporal(TemporalType.TIMESTAMP)public Date getCreateTime() {return createTime;}public void setCreateTime(Date createTime) {this.createTime = createTime;}}
student

package spring.hibernate.model;import java.util.List;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.FetchType;import javax.persistence.JoinColumn;import javax.persistence.ManyToMany;import javax.persistence.ManyToOne;import javax.persistence.OneToOne;import javax.persistence.Table;@Entity@Table(name="student")public class Student extends BaseEntity{private String name;privateSchool school;private List<Teacher> teachers;private IDcard iDcard;@Column(name="name")public String getName() {return name;}public void setName(String name) {this.name = name;}@ManyToOne(fetch=FetchType.LAZY)@JoinColumn(name="school_id")public School getSchool() {return school;}public void setSchool(School school) {this.school = school;}@ManyToMany(mappedBy = "students", fetch = FetchType.LAZY)public List<Teacher> getTeachers() {return teachers;}public void setTeachers(List<Teacher> teachers) {this.teachers = teachers;}@OneToOnepublic IDcard getiDcard() {return iDcard;}public void setiDcard(IDcard iDcard) {this.iDcard = iDcard;}}
school

package spring.hibernate.model;import java.util.List;import javax.persistence.Entity;import javax.persistence.FetchType;import javax.persistence.OneToMany;import javax.persistence.Table;@Entity@Table(name="school")public class School extends BaseEntity{private String name;List<Student> students;public String getName() {return name;}public void setName(String name) {this.name = name;}@OneToMany(fetch=FetchType.LAZY,mappedBy="school")public List<Student> getStudents() {return students;}public void setStudents(List<Student> students) {this.students = students;}}
teacher

package spring.hibernate.model;import java.util.List;import javax.persistence.Entity;import javax.persistence.FetchType;import javax.persistence.JoinColumn;import javax.persistence.JoinTable;import javax.persistence.ManyToMany;import javax.persistence.Table;@Entity@Table(name="teacher")public class Teacher extends BaseEntity{private String name;private List<Student> students;public String getName() {return name;}public void setName(String name) {this.name = name;}@ManyToMany(fetch = FetchType.LAZY)@JoinTable(name = "teacher_student", joinColumns = @JoinColumn(name = "teacher_id"), inverseJoinColumns = @JoinColumn(name = "student_id"))public List<Student> getStudents() {return students;}public void setStudents(List<Student> students) {this.students = students;}}
idcard

package spring.hibernate.model;import javax.persistence.Entity;import javax.persistence.FetchType;import javax.persistence.OneToOne;import javax.persistence.Table;@Entity@Table(name="id_card")public class IDcard extends BaseEntity{private Student student;@OneToOne(fetch=FetchType.LAZY,mappedBy="iDcard")public Student getStudent() {return student;}public void setStudent(Student student) {this.student = student;}}
几个model之间的联系

student-school 多对一
student-idcard 一对一
sutdent-teacher 多对多

jpa相关注解解释:

@MappedSuperclass : 标注超类,不参与印射,但其属性将被子类集成且与数据库印射
@Id主键标识,对标识@Entity的类必须有主键(多对多关系表没有)
@GeneratedValue : 主键生成策略, table(表主键),sequence(序列主键),identity(自增主键),auto(程序主键)
@Column : 属性映射,model的property与数据库的映射表达,可为空,默认的映射相关属性
name : 映射数据库列名,根据命名策略(可设置,默认属性名)生成
unique : 是否唯一,default false
nullable : 是否可以为空,default true
insertable :是否允许插入,default true
updatable :是否允许被更新,default true
columnDefinition :列定义(比如设置一些默认值什么的)
length: 默认长度255
precision:针对decimal默认精度,default 0

@Temporal :映射时间格式-空(不写此注解),默认TIMESTAMP(对应数据库date_time,YYYY-MM-DD HH:MM:SS),有三种可选择TIMESTAMP(date_time YYYY-MM-DD),DATE(date),Time(time HH:MM:SS),此注解作用于添加与查询,值得注意的是,你设置为Date,存入是时间精确到天,实际时间为YYYY-MM-DD 00:00:00,此外顺便提一下datetime与timestamp的区别,datetime占用8字节,时间范围更大(1000年-9999年),而timestamp占用4字节,时间范围相对较小(1970年-2038年).
此外有一点需要注意,mysql默认是不区分大小写,所以name大小写都可(个人习惯于数据库保持一致,java中这个属于常量范围,一般用大写标识)


@Entity : 表明映射对象
@Table: table相关信息,可不标注
@OneToOne :一对一,相关属性
targetEntity:关联类,一般无需设置,指的就是成员本身
cascade : 级联,all(所有),persist(保存),merge(合并),remove(删除),refresh(刷新),detach(分离)
fetch: 默认eager-立即加载,lazy-延迟加载(立即加载与延迟加载的区别是,立即加载拥有关联对象中全部信息,延迟加载只有一个id)
optional:关联关系是否可以为空,默认为true
mappedBy:关系维护方,在关联关系中,OneToOne 关系是允许双方维护的,这里指的是被维护方在维护方的属性名,标注在被维护方.@onetomany 只能有多的一方维护.

@manytomany可以双方维护,注意分清维护与级联直接的区别,一个针对关系(只操作关系),一个是针对类之间关联,比如解除关系,记录还在,但级联删除,记录全删.

@JoinColumn 映射关联类(@column 不能与@onetoone @manytonone @manytomany一起,一般配合@joincolumn使用)
name : 被关联类映射数据列名
referencedColumnName : 映射关联类的列名(默认空)
foreignKey: 关联主键,referencedColumnName指定,就无效
unique,nullable,insertable,updatable,columnDefinition与@column功能一样.
@manyToone : targetEntity,cascade,fetch,optional 功能与@onetoone中相似

@ManyToMany : targetEntity , cascade, fetch,mappedBy 功能与@onetoone中相似,关系可双方维护,设置了mappedBy才可进行关系维护,关系存在第三张表
@JoinTable 与ManyToMany一起使用,可不用配(默认配置,关联其主键) 注意joinColumns与inverseJoinColumns别写反了,前者关联当前类

name:保存关联关系表名
joinColumns:使用@JoinColumn关联当前类与关系表映射关系.可配置多个
@JoinColumn:name-在关系表中的列名名,foreignKey-关联主键(默认主键)
inverseJoinColumns:使用@JoinColumn关联另一方与关系表映射关系.可配置多个


基本使用

简单查询

@Test@Transactionalpublic void set(){List<School> result;/*#############基本查询###############*//*hibernateTemplate 与query 查询功能很相似,此处主要以hibernateTemplate为例*//*1.通过HQL语句查询(HQL语法与sql语法相似,有兴趣详细了解可上网查询) * HQL语句单表查询可以省略SELECT T FROM ,需要注意的是HQL 不支持SELECT * FROM  * 关键字不区分大小写(规范书写,sql关键字一律大写),但是类名必须书写正确(因为要根据类名寻找相应的class,假如多个类名重名,要带上包名) * *///通过    ?   find(String queryString, Object... values) Object[] paramValues =new Object[]{"a",1};String hql ="";/*问好出现的顺序依次对应后面values(暂时没找到怎么根据索引对应相应的值)--这个方式以及过时*/hql = "FROm School s WHERE s.name=? AND s.id=? ORDER BY s.name";result= (List<School>) hibernateTemplate.find(hql, paramValues);System.out.println("template? 占位符:"+result.get(0).getName()); /*query jpa占位符,可以根据?索引的位置设置响应的value值*/hql = "FROm School s WHERE s.name=? AND s.id=? ORDER BY s.name";Query query = hibernateTemplate.getSessionFactory().getCurrentSession().createQuery(hql);query.setParameter(0, paramValues[0]);query.setParameter(1, paramValues[1]);result = query.list();System.out.println("query ? 占位符:"+result.get(0).getName());//通过    :+参数别名 findByNamedParam(String queryString, String[] paramNames, Object[] values) --推荐String[] paramNames =new String[]{"schoolName","schoolId"};/*queryString中括号不是必须的只是为了更好的区分*/hql = "FROM School s WHERE s.name=(:schoolName) AND s.id=(:schoolId) ORDER BY s.name";result= (List<School>) hibernateTemplate.findByNamedParam(hql, paramNames,paramValues);System.out.println("template参数 占位符:"+result.get(0).getName());/*query*/query = hibernateTemplate.getSessionFactory().getCurrentSession().createQuery(hql);query.setParameter(paramNames[0], paramValues[0]);query.setParameter(paramNames[1], paramValues[1]);result = query.list();System.out.println("query参数 占位符:"+result.get(0).getName());/*2.通过Example--(不支持主键.null.空指针,排序...*/School exampleEntity = new School();exampleEntity.setId(1);exampleEntity.setName("a");exampleEntity.setCreateTime(null);result = hibernateTemplate.findByExample(exampleEntity);System.out.println("example参数 占位符:"+result.get(0).getName());/*3.通过queryName--hibernateTemplate.findByNamedQuery(queryName, values) * 这种要求在xml中定义好: * <query name="queryName"><![定义查询语句]]></query>  * 这种用法用的少忽略 * *//*3.通过DetachedCriteria--*/DetachedCriteria criteria = DetachedCriteria.forClass(School.class);criteria.add(Restrictions.eq("id", 1));criteria.add(Restrictions.eq("name", "a"));criteria.addOrder(Order.desc("name"));System.out.println("criteria 查询:"+result.get(0).getName());/*4.通过原生sql查询--*/String sql= "SELECT * FROM school s where s.id=1 AND s.name='a'";SQLQuery sQLQuery = hibernateTemplate.getSessionFactory().getCurrentSession().createSQLQuery(sql);sQLQuery.addEntity(School.class);sQLQuery.setResultTransformer(Criteria.ROOT_ENTITY);result = sQLQuery.list();System.out.println("sql 查询:"+result.get(0).getName());/*#############返回的结果集###############*//*1.以上结果集返回List<Object> 集合*//*#############指定查询字段,分页,函数,连表,group by 等###############*//* 1.HQL 与sql 指定查询字段,分页 ,连表,函数,以及group by 直接在语句中写,很简单,而criteria就相对比较灵活(criteria单独讲) * 2.criteria分页,使用 hibernateTemplate.findByCriteria(criteria, firstResult, maxResults) * */System.out.println("success!");}
上述查询返回值为List<Object[]>,但都可以强转为List<School>(sQLQuery除外),其实HQL省略SELELCT 前缀是可以强转,但一旦使用了SELECT强转就会失败,在query或criteria可以设置类型转换.

query,criteria设置结果集转换

//原生sqlString sql= "SELECT s.id,s.name,s.create_time as createTime FROM school s where s.id=1 AND s.name='a'";SQLQuery sQLQuery = hibernateTemplate.getSessionFactory().getCurrentSession().createSQLQuery(sql);/*转换方式三种(暂时只考虑单表) * 默认Object[]数组 * Transformers.aliasToBean(School.class)--实体bean * Transformers.ALIAS_TO_ENTITY_MAP --map * Transformers.TO_LIST --List * 注意的是:map中key是列的别名, 列的别名(没有别名就去数据本身字段名)要与bean 的property一致 * */sQLQuery.setResultTransformer(Transformers.aliasToBean(School.class));List<School> sqllist = sQLQuery.list();System.out.println("sql 查询:"+sqllist.get(0).getName());//HQL hql与sql相同,因为setResultTransformer是共同父类Query中的方法String hql = "SELECT s.id,s.name FROM School s";Query hqlQuery = hibernateTemplate.getSessionFactory().getCurrentSession().createQuery(hql);hqlQuery.setResultTransformer(Transformers.aliasToBean(School.class));List<School> hqllist = hqlQuery.list();System.out.println("hql 查询:"+hqllist.get(0).getName());DetachedCriteria criteria = DetachedCriteria.forClass(School.class);criteria.setProjection(Projections.projectionList().add(Projections.property("id"),"id").add(Projections.property("name"),"name")).setResultTransformer(Transformers.aliasToBean(School.class));List<School> crilist = (List<School>) hibernateTemplate.findByCriteria(criteria);System.out.println("criteria 查询:"+crilist.get(0).getName());

query,criteria相关函数使用(仅以sum group by举例)

/*函数举例仅以sum group by举例*///原生sql与hql用法相似,此处仅举hql,hql最后也是要转换为sql语句,hql基本都支持sql中的函数且名字都没有怎么换String hql = "SELECT SUM(s.id) as sumId,s.name FROM School s GROUP BY s.name";Query hqlQuery = hibernateTemplate.getSessionFactory().getCurrentSession().createQuery(hql);hqlQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);List<Map<String, Object>> hqllist = hqlQuery.list();//sum() avg(),count()等相关函数,查询结果都为long类型(以前低版本是integer)System.out.println("hql 查询:"+hqllist.get(0).get("sumId"));//criteria/*小提示 * Projections.property 根据数据库类型转成相应的java类型 * Projections.sqlProjection 可以将根据types 将转为的java类型再转换为指定类型 * */DetachedCriteria criteria = DetachedCriteria.forClass(School.class);criteria.setProjection(Projections.projectionList().add(Projections.sum("id"),"sumId").add(Projections.groupProperty("name"),"name")).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);List<Map<String, Object>> crilist =  (List<Map<String, Object>>) hibernateTemplate.findByCriteria(criteria);System.out.println("criteria 查询:"+crilist.get(0).get("sumId"));//criteria 还有另一张groupby方法criteria.setProjection(null);criteria.setProjection(Projections.sqlGroupProjection("SUM({alias}.id) as sumId,{alias}.name as schoolname", "schoolname", new String[] {"sumId","schoolname"}, new Type[] {IntegerType.INSTANCE,StringType.INSTANCE } )).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); crilist =  (List<Map<String, Object>>) hibernateTemplate.findByCriteria(criteria);System.out.println("criteria 查询:"+crilist.get(0).get("sumId"));

讲了单表,再来看看连表查询

//原生sqlString sql= "SELECT s.name as schoolName,st.name as studentName FROM school s,student st where s.id= st.school_id";SQLQuery sQLQuery = hibernateTemplate.getSessionFactory().getCurrentSession().createSQLQuery(sql);sQLQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);List<Map<String, Object>> sqllist = sQLQuery.list();System.out.println("sql 查询:"+sqllist.get(0).get("schoolName"));//HQL除了与上述sql一样的写法外,还有自带特色连表(默认是内连接)String hql= "SELECT st.school.name as schoolName,st.name as studentName FROM Student st";Query hqlQuery = hibernateTemplate.getSessionFactory().getCurrentSession().createQuery(hql);hqlQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);List<Map<String, Object>> hqllist = hqlQuery.list();System.out.println("hql 查询:"+hqllist.get(0).get("schoolName"));/*打印的sql语句    *  select        school1_.name as col_0_0_,        student0_.name as col_1_0_     from        student student0_,        school school1_     where        student0_.school_id=school1_.id*///criteriaDetachedCriteria criteria = DetachedCriteria.forClass(Student.class,"s");criteria.createAlias("school", "st", JoinType.INNER_JOIN).setProjection(Projections.projectionList().add(Projections.property("s.name"),"studentName").add(Projections.property("st.name"),"schoolName")).add(Restrictions.eqProperty("s.name", "st.name")).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);List<Map<String, Object>> criterialist = (List<Map<String, Object>>) hibernateTemplate.findByCriteria(criteria);System.out.println("criteria 查询:"+criterialist.get(0).get("schoolName"));
以上讲了用hibernateTemplate,sql,hql,DetachedCriteria(离线查询)查询,其实还有另外还有在线查询Criteria,Criteria与DetachedCriteria的主要区别,前者跟当前session绑定,而后者可供任何session使用,两者用法相似,此处不一一进行描述.此外,JPA的entityManager的用法跟模板使用有些类似,有时间再去总结总结.
上述讲了hibernate的常用,我们也会关注hibernate的一些关键词,状态,session,缓存.以上用到jpa,pa与hibernate之间的关系(hibernate与jpa)

hibernate有三种状态(hibernate三种状态)transient(瞬时状态),persistent(持久化状态)以及detached(离线状态),但jpa有四种状态new(新建),managed(托管),detached(游离),removed(删除),状态都与hibernate有对应关系(new与removed对应transient),鉴于jpa与hibernate状态有对应关系,现仅以hibernate状态为例

hibernate状态转换图:


还有把transient中new 与remove区分开来


理解hibernate的三种状态:

1.瞬时--刚new出来,或删除

2.持久--当前与session有关联,并且相关联session没有关闭,事务没有提交

3.托管 --没有当前session与之关联

其中有个session的概念十分重要,session是hibernate的一个句柄对象,hibernate实体的操作基本上都是通过时session完成,每次请求都会分配一个session(session是轻量级的),但是session不是线程安全的,所以除了查询操作外,增,添,改基本都会要求使用同一个session(使用事务,保证同一个session,spring集成hibernate session管理),每个session都有一个缓存,习惯称之为一级缓存,每次请求都会有自己的session从而拥有自己的一级session缓存.

session缓存减少了与数据库交互的次数,从内存中查询数据提高效率,session也需要保证缓存数据与数据库的数据同步,因此,一次更改删除都会更新session缓存(add,update,delete必须有事务,事务提交会进行检查,不一致更新缓存数据),因此session同步的相关操作就变得比较重要,必要时,需要手动清理缓存(clear-清理所有,flush-刷新所有,refreshs(T)-刷新单个,reevict(T)-清理单个).通过setflushmode控制刷新时机


hibernate中的事务,hibernate不配置,默认使用数据库的默认级别(mysql-REPEATABLE READ(4),oracle-READ COMMITTED(2)),稍微回顾下事务级别:

READ UNCOMMITTED:脏读、不可重复读、虚读都有可能发生。

READ COMMITTED:避免脏读,不可重复读、虚读都有可能发生。

REPEATABLE READ:避免脏读、不可重复读,虚读有可能发生。

SERIALIZABLE:避免脏读、不可重复读、虚读的发生。

更改hibernate隔离级别,通过hibernate.connection.isolation=1|2|4|8 设置.我们用同一个session保证了数据正确性,但在并发的时候,我们也要考虑数据更改的正确性,hibernate实现了悲观与乐观锁

悲观锁实现很容易,hibernateTemplate 操作有个别备选参数,LockMode,设置lockMode=LockMode.PESSIMISTIC_WRITE即可,但是使用乐观锁,还需要指定相关的version(依据乐观锁的原理,有个判定依据verison可以是版本号,也可以是时间戳)

@OptimisticLocking(type=OptimisticLockType.ALL)-标注model中

type 可选

VERSION,检查@javax.persistence.Version指定的属性(默认值)

NONE,禁用任何版本检查(即使指定了@javax.persistence.Version属性)

ALL,检查所有属性

DIRTY,只检查变化了的属性

@Version 标注在版本号或时间戳中

使用的LockMode

/** * No lock required. If an object is requested with this lock * mode, a <tt>READ</tt> lock will be obtained if it is * necessary to actually read the state from the database, * rather than pull it from a cache.<br> * <br> * This is the "default" lock mode. */NONE( 0, "none" ),/** * A shared lock. Objects in this lock mode were read from * the database in the current transaction, rather than being * pulled from a cache. */READ( 5, "read" ),//共享锁/** * An upgrade lock. Objects loaded in this lock mode are * materialized using an SQL <tt>select ... for update</tt>. * * @deprecated instead use PESSIMISTIC_WRITE */@DeprecatedUPGRADE( 10, "upgrade" ),//悲观锁/** * Attempt to obtain an upgrade lock, using an Oracle-style * <tt>select for update nowait</tt>. The semantics of * this lock mode, once obtained, are the same as * <tt>UPGRADE</tt>. */UPGRADE_NOWAIT( 10, "upgrade-nowait" ),/** * Attempt to obtain an upgrade lock, using an Oracle-style * <tt>select for update skip locked</tt>. The semantics of * this lock mode, once obtained, are the same as * <tt>UPGRADE</tt>. */UPGRADE_SKIPLOCKED( 10, "upgrade-skiplocked" ),/** * A <tt>WRITE</tt> lock is obtained when an object is updated * or inserted.   This lock mode is for internal use only and is * not a valid mode for <tt>load()</tt> or <tt>lock()</tt> (both * of which throw exceptions if WRITE is specified). */WRITE( 10, "write" ),//等待锁/** * Similar to {@link #UPGRADE} except that, for versioned entities, * it results in a forced version increment. * * @deprecated instead use PESSIMISTIC_FORCE_INCREMENT */@DeprecatedFORCE( 15, "force" ),/** *  start of javax.persistence.LockModeType equivalent modes *//** * Optimistically assume that transaction will not experience contention for * entities.  The entity version will be verified near the transaction end. */OPTIMISTIC( 6, "optimistic" ),//乐观锁/** * Optimistically assume that transaction will not experience contention for * entities.  The entity version will be verified and incremented near the transaction end. */OPTIMISTIC_FORCE_INCREMENT( 7, "optimistic_force_increment" ),/** * Implemented as PESSIMISTIC_WRITE. * TODO:  introduce separate support for PESSIMISTIC_READ */PESSIMISTIC_READ( 12, "pessimistic_read" ),/** * Transaction will obtain a database lock immediately. * TODO:  add PESSIMISTIC_WRITE_NOWAIT */PESSIMISTIC_WRITE( 13, "pessimistic_write" ),/** * Transaction will immediately increment the entity version. */PESSIMISTIC_FORCE_INCREMENT( 17, "pessimistic_force_increment" );
hibernate相关一些方法区别:

get与load: load会延迟加载,查询不到会报异常,get找不到返回null

update与merge:update会直接更新,merge会先查询,结果不一致才会执行更新

save与persist:save会立即产生持久化标识符并返回,persist不保证立即产生持久化标识,无返回值

 find与iterate: 1.iterate会自动查询二级缓存,find需手动开启二级缓存(例如hibernateTemplate.setCacheQueries(true ))
2.iterate查询策略(1+n,先查出id列表,返回的是代理的对象(延迟加载,与load类似),当需要加载对象时,根据对象id去缓存中查,查不到去数据库查,最多发出1+n条查询), find直接查(对于相同的查询,如果开启了二级缓存,先从缓存中取,再从数据库中取),最多发出1条查询语句
3.iterate使用不常用的结果集,频繁读写的操作使用find

session作为一级缓存,存储在内存中,hibernate也支持二级缓存,hibernate二级缓存是sessionfactory级别的缓存,线程安全,所有session共享


配置二级缓存以ehcache为例

<!--二级缓存配置 --><!-- 开启查询缓存 --><prop key="hibernate.cache.use_query_cache">true</prop><!-- 开启二级缓存 --><prop key="hibernate.cache.use_second_level_cache">true</prop><!-- 高速缓存提供程序 --><!-- 缓存管理器 --><prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
ehcache.xml配置(ehcache服务器启动会自动读取此配置文件)

<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"><!--数据存储位置 --><diskStore path="${java.io.tmpdir}/cache" /><!-- Mandatory Default Cache configuration. These settings will be applied to caches created programmtically using CacheManager.add(String cacheName) --><!-- 关于时间的数字单位(s) maxElementsInMemory 内存中最多允许保存的数据对象的数量maxElementsOnDisk 在磁盘上缓存的element的最大数目,默认值为0,表示不限制。 external 缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期;如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断 timeToIdleSeconds 对象空闲时间,指对象在多长时间没有被访问就会失效。 默认值0,表示一直可以访问。 timeToLiveSeconds 对象存活时间,指对象从创建到失效所需要的时间。默认值0,表示一直可以访问。 overflowToDisk 内存不足时,是否启用磁盘缓存 diskPersistent 是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。diskExpiryThreadIntervalSeconds: 对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。 diskSpoolBufferSizeMB: DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。memoryStoreEvictionPolicy 内存不足时数据对象的清除策略 ,默认值LRU ehcache中缓存的3种清空策略: FIFO(first in first out):先进先出 LFU(Less Frequently Used):一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。LRU(Least Recently Used):最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --><!--默认cache --><defaultCache maxElementsInMemory="10000"maxElementsOnDisk="10000000" eternal="false" timeToIdleSeconds="120"timeToLiveSeconds="120" diskPersistent="false" overflowToDisk="true"diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"></defaultCache><cache name="org.hibernate.cache.spi.UpdateTimestampsCache"maxElementsInMemory="5000" eternal="true" overflowToDisk="true" /><cache name="org.hibernate.cache.internal.StandardQueryCache"maxElementsInMemory="10000" eternal="false" timeToLiveSeconds="120"overflowToDisk="true" /><!-- java文件注解查找cache方法名的策略:如果不指定java文件注解中的region="ehcache.xml中的name的属性值", 则使用name名为com.lysoft.bean.user.User的cache(即类的全路径名称), 如果不存在与类名匹配的cache名称, 则用 defaultCache 如果User包含set集合, 则需要另行指定其cache,例如User包含citySet集合, 则也需要添加配置到ehcache.xml中 --><!-- maxElementsInMemory,name,external ,overflowToDiskb必须配置 --><!-- school缓存 --><cache name="schoolCache" maxElementsInMemory="10000"eternal="false" timeToLiveSeconds="600" overflowToDisk="false" /></ehcache>
school

package spring.hibernate.model;import java.util.Date;import java.util.List;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.FetchType;import javax.persistence.OneToMany;import javax.persistence.Table;import javax.persistence.Temporal;import javax.persistence.TemporalType;import javax.persistence.Version;import org.hibernate.annotations.Cache;import org.hibernate.annotations.CacheConcurrencyStrategy;import org.hibernate.annotations.OptimisticLocking;@Entity@Table(name="school")@OptimisticLocking@Cache(region="schoolCache",usage=CacheConcurrencyStrategy.READ_ONLY,include="all")public class School extends BaseEntity{private String des;private String name;private Date updateTime;    List<Student> students;public String getDes() {return des;}public void setDes(String des) {this.des = des;}public String getName() {return name;}public void setName(String name) {this.name = name;}@OneToMany(fetch=FetchType.LAZY,mappedBy="school")public List<Student> getStudents() {return students;}public void setStudents(List<Student> students) {this.students = students;}@Column(name = "update_time")@Temporal(TemporalType.TIMESTAMP)@Versionpublic Date getUpdateTime() {return updateTime;}public void setUpdateTime(Date updateTime) {this.updateTime = updateTime;} }
@cache 表示此对象加入二级缓存(@cache,hibernate属于类级别缓存,spring也有方法级别的缓存--spring缓存)

rejion:对应ehcache.xml中cache的name

usage:缓存策略:NONE, READ_ONLY, NONSTRICT_READ_WRITE, TRANSACTIONAL

include默认all,还有一种non-lazy

可以看到jpa中,一些基本操作只要知道其class,就可以进行相关通用操作,我们可以使用泛型来达到提取共用方法的目的.

BaseDaoImpl

public class BaseDaoImpl<T, ID extends Serializable> implements BaseDao<T, ID> {/** 实体类类型 */private Class<T> entityClass;@Autowiredprotected HibernateTemplate hibernateTemplate;@Resource(name = "dataSource")protected DataSource datasource;public BaseDaoImpl() {Type type = getClass().getGenericSuperclass();Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();entityClass = (Class<T>) parameterizedType[0];}@Overridepublic Serializable save(T entity) {Assert.notNull(entity);return hibernateTemplate.save(entity);}}
SchoolDaoImpl
public class SchoolDaoImpl extends BaseDaoImpl<Label, Integer> implements LabelDao {}
使用时直接schoolDao.save(school),不需要指定相应的class了.


原创粉丝点击