hibernate(jpa)+spring

本文详细介绍了Hibernate ORM框架的使用,包括其对象关系映射原理,与Mybatis的对比,以及在项目中如何配置和使用Hibernate。文章强调了JPA注解在简化对象关联关系处理上的优势,并提供了JPA注解的解析,如@MappedSuperclass, @Id, @GeneratedValue等。此外,还探讨了Hibernate的持久化状态、事务管理、悲观锁与乐观锁的概念,以及一级缓存和二级缓存的使用策略。" 137040113,7337247,大语言模型在异常检测中的精调实践,"['深度学习', '神经网络', '大数据', '人工智能', '大型语言模型']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目中用到了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;

@MappedSuperclass
public 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;
	private	School 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;
	}
	@OneToOne
	public 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
	@Transactional
	public 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设置结果集转换

		//原生sql
		String 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"));

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

		//原生sql
		String 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*/
		
		//criteria
		DetachedCriteria 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
		 */
		@Deprecated
		UPGRADE( 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
		 */
		@Deprecated
		FORCE( 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)
	@Version
	public 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;

	@Autowired
	protected HibernateTemplate hibernateTemplate;

	@Resource(name = "dataSource")
	protected DataSource datasource;

	public BaseDaoImpl() {
		Type type = getClass().getGenericSuperclass();
		Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
		entityClass = (Class<T>) parameterizedType[0];
	}

	@Override
	public 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了.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值