hibernate基础

1.简介与入门

2.Session 概述

3.对象的基本操作

4.常用注解

5.多表级联

6.HQL 查询

7.Criteria 查询

8.缓存

9.调用存储过程

10.锁机制

11.与 spring 整合

1.简介与入门

Hibernate是一个开源,轻量级的ORM(对象关系映射)工具。Hibernate框架简化了java应用程序与数据库交互的开发。ORM工具简化了数据创建,数据处理和数据访问。它是将对象映射到数据库中存储的数据(表)的编程技术。

Hibernate框架有很多优点。它们分别如下:

  • 开源和轻量级: Hibernate框架是根据LGPL许可证和轻量级的开源工具。
  • 快速性能: Hibernate框架的性能很快,因为缓存在Hibernate框架内部使用。 hibernate框架中有两种类型的缓存:一级缓存和二级缓存。一级缓存默认是启用的。
  • 数据库独立查询: HQL(Hibernate查询语言)是面向对象的SQL版本。 它生成数据库独立查询。 所以你不需要编写数据库特定的查询语句。 在Hibernate之前,如果项目更改了数据库,我们需要更改SQL查询,从而导致维护变得非常复杂。
  • 自动创建表: Hibernate框架提供了自动创建数据库表的功能。 因此,无需手动在数据库中创建表。
  • 简化复杂连接: 在hibernate框架中可轻松获取多个表中的数据。
  • 提供查询统计和数据库状态: Hibernate支持查询缓存,并提供有关查询和数据库状态的统计信息。

添加相关依赖


<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.10.Final</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.6</version>
</dependency>

在 resource 目录下创建 logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--控制台-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p --- [%t] %-40.40logger{39} : %m%n</pattern>
        </encoder>
    </appender>
    <!--根logger 日志级别排序为:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="INFO" additivity="false">
        <appender-ref ref="console"/>
    </root>
</configuration>

在 resource 目录下创建 hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>

        <!-- 配置连接数据库的基本信息 -->
        <property name="connection.username">root</property>
        <property name="connection.password">123456</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql:///hibernate</property>

        <!-- hibernate 所使用的数据库方言 -->
        <property name="dialect">org.hibernate.dialect.MySQL57Dialect</property>

        <!-- 使用mysql innodb 引擎-->
        <property name="hibernate.dialect.storage_engine">innodb</property>

        <!-- 执行操作时是否在控制台打印 SQL -->
        <property name="show_sql">true</property>

        <!-- 表生成策略 -->
        <property name="hbm2ddl.auto">update</property>

        <!-- 指定关联的 .hbm.xml 文件 -->
        <mapping resource="News.hbm.xml"/>

    </session-factory>
</hibernate-configuration>

数据库方言:

数据库类型        	        Hibernate sql方言
DB2                	        org.hibernate.dialect.DB2Dialect
DB2 AS/400        	        org.hibernate.dialect.DB2400Dialect
DB2 OS390	                org.hibernate.dialect.DB2390Dialect
PostgreSQL 8.1	                org.hibernate.dialect.PostgreSQL81Dialect
PostgreSQL 8.2 and later	org.hibernate.dialect.PostgreSQL82Dialect
MySQL5	                        org.hibernate.dialect.MySQL5Dialect
MySQL5 with InnoDB	        org.hibernate.dialect.MySQL5InnoDBDialect
MySQL with MyISAM	        org.hibernate.dialect.MySQLMyISAMDialect
Oracle (any version)	        org.hibernate.dialect.OracleDialect
Oracle 9i	                org.hibernate.dialect.Oracle9iDialect
Oracle 10g	                org.hibernate.dialect.Oracle10gDialect
Oracle 11g	                org.hibernate.dialect.Oracle10gDialect
Sybase ASE 15.5	                org.hibernate.dialect.SybaseASE15Dialect
Sybase ASE 15.7	                org.hibernate.dialect.SybaseASE157Dialect
Sybase Anywhere	                org.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Server 2000	org.hibernate.dialect.SQLServerDialect
Microsoft SQL Server 2005	org.hibernate.dialect.SQLServer2005Dialect
Microsoft SQL Server 2008	org.hibernate.dialect.SQLServer2008Dialect
SAP DB	                        org.hibernate.dialect.SAPDBDialect
Informix	                org.hibernate.dialect.InformixDialect
HypersonicSQL	                org.hibernate.dialect.HSQLDialect
H2 Database	                org.hibernate.dialect.H2Dialect
Ingres	                        org.hibernate.dialect.IngresDialect
Progress	                org.hibernate.dialect.ProgressDialect
Mckoi SQL	                org.hibernate.dialect.MckoiDialect
Interbase	                org.hibernate.dialect.InterbaseDialect
Pointbase	                org.hibernate.dialect.PointbaseDialect
FrontBase	                org.hibernate.dialect.FrontbaseDialect
Firebird	                org.hibernate.dialect.FirebirdDialect

实体

public class News {	
	private Integer id;
	private String title;
	private String content;
......
}

创建 News.hbm.xml 该文件将实体的属性映射到数据表,从而完成数据库创表和和保存对象的操作

<?xml version="1.0"  encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.hx.entity">
    <class name="News" table="NEWS" dynamic-insert="true">
        <!--dynamic-insert="true" 时表示插入对象时空的字段不插入 -->
        <id name="id" type="java.lang.Integer">
            <column name="ID"/>
            <generator class="native"/>
            <!-- 指定主键的生成策略, native: 使用数据库本地方式 -->
        </id>
        <property name="title" not-null="true"
                  length="50" type="java.lang.String" column="TITLE">
        </property>
        <property name="content">
            <column name="CONTENT" sql-type="text"></column>
            <!--数据库改字段的名字和类型-->
        </property>
    </class>
</hibernate-mapping>

执行会自动创表,插入数据

// 1.创建SessionFactory
Configuration configuration=new Configuration().configure();
SessionFactory sessionFactory=configuration.buildSessionFactory();
// 2. 获得一个 Session 对象
Session session = sessionFactory.openSession();
// 3. 开启事务
Transaction transaction = session.beginTransaction();
// 4. 执行保存操作
News news = new News("标题","正文");
session.save(news);		
// 5. 提交事务
transaction.commit();
// 6. 关闭 Session
session.close();		
// 7. 关闭 SessionFactory 对象
sessionFactory.close();
System.out.println(news);

主键生成策略

increment:代理主键,适合于所有数据库,由hibernate维护主键自增,和底层数据库无关,但是不适合于2个或以上hibernate进程。

identity:代理主键,适合于Mysql或ms sql server等支持自增的dbms,主键值不由hibernate维护。
sequence:代理主键,适合于oracle等支持序列的dbms,主键值不由hibernate维护,由序列产生。
native:代理主键,根据底层数据库的具体特性选择适合的主键生成策略,如果是mysql或sqlserver,选择identity,如果是oracle,选择sequence。
hilo:代理主键,hibernate把特定表的字段作为hign值,生成主键值
uuid.hex:代理主键,hibernate采用uuid 128位算法生成基于字符串的主键值
assigned:适合于由java指定主键,一般使用uuid,保存对象时如果主键为空会抛异常。

表生成策略

–create : 会根据 .hbm.xml  文件来生成数据表,但是每次运行都会删除上一次的表,重新生成表,哪怕二次没有任何改变

–create-drop : 会根据.hbm.xml文件生成表,但是SessionFactory一关闭,表就自动删除

–update : 最常用的属性值,也会根据.hbm.xml文件生成表,但若.hbm.xml  文件和数据库中对应的数据表的表结构不同,Hiberante  将更新数据表结构,但不会删除已有的行和列

–validate : 会和数据库中的表进行比较,若.hbm.xml文件中的列在数据表中不存在,则抛出异常

数据库字段类型

hibernate的映射关系如下

Hibernate 映射类型           java 类型                                标准 sql 类型
integer                      int or Integer                           INTEGER 
long                         long or java.lang.Long                   BIGINT  
short                        short or java.lang.Short                 SMALLINT  
float                        float or java.lang.Float                 FLOAT              
double                       double or java.lang.Double               DOUBLE  
big_decimal                  java.math.BigDecimal                     NUMERIC  
character                    java.lang.String                         CHAR(1)  
string                       java.lang.String                         VARCHAR 
byte                         byte or java.lang.Byte                   TINYINT 
boolean                      boolean or java.lang.Boolean             BIT  
yes_no                       boolean or java.lang.Boolean             CHAR(1)('Y' or 'N')  
true_false                   boolean or java.lang.Boolean             CHAR(1)('Y' or 'N') 
date                         java.util.Date or java.sql.Date          DATE  
time                         java.util.Date or java.sql.Time          TIME  
timestamp                    java.util.Date or java.sql.TimeStamp     TIMESTAMP  
calendar                     java.util.Calendar                       TIMESTAMP  
calendar_date                java.util.Calendar                       DATE  
binary                       byte[]                                   VARBINARY( or BLOB)  
text                         java.lang.String                         CLOB  
serializable                 java.io.Serializable                     VARBINARY (or BLOB)
clob                         java.sql.Clob                            CLOB  
blob                         java.sql.Blob                            BLOB  
class                        java.lang.Class                          VARCHAR  
locale                       java.util.Locale                         VARCHAR  
timezone                     java.util.TimeZone                       VARCHAR  
currency                     java.util.Currency                       VARCHAR
2.Session 概述

为了方便我们使用junit的测试前后注解

private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;

@Before
public void init() {
    Configuration configuration=new Configuration().configure();
    sessionFactory=configuration.buildSessionFactory();
    session = sessionFactory.openSession();
    transaction = session.beginTransaction();
}

@After
public void destroy() {
    transaction.commit();
    session.close();
    sessionFactory.close();
}

Session 接口是 Hibernate 向应用程序提供的操纵数据库的最主要的接口,它提供了基本的保存,更新,删除和加载 Java 对象的方法。

/*
增加
 */
@Test
public void test01() {
    News news = new News("标题2", "正文2");
    session.save(news);
}

/*
删除
*/

@Test
public void test02() {
    News news = session.get(News.class, 5);
    session.delete(news);
    System.out.println(news);
}

/*
修改
 */
@Test
public void test03() {
    News news = new News("标题2", "正文2");
    news.setId(2);
    session.saveOrUpdate(news);
}
/*
查询
*/

@Test
public void test04() {
    News news1 = session.get(News.class, 2);
    System.out.println(news1);
    News news2 = session.get(News.class, 2);
    System.out.println(news2);
}

Session 具有一个缓存(一级缓存),位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应。Session 能够在某些时间点,按照缓存中对象的变化来执行相关的 SQL 语句,来同步更新数据库,这一过程被称为刷新缓存(flush)

一级缓存(基于session的缓存)一级缓存 hibernate 默认开启,一级缓存表示 session 不关闭的情况下,该 session 保存的对象多次查询从缓存中获取。

News news = (News) session.get(News.class, 1);
System.out.println(news);

News news2 = (News) session.get(News.class, 1);
System.out.println(news2);

如上:当第二次查询时不在去数据库里面查询,而是从 session 里面获取,共发一次 sql。除非中间刷新缓存session.flush();关闭session 和session.clear();

对象的四个状态

站在持久化的角度,Hibernate 把对象分为 4 种状态:持久化状态、临时状态、游离状态和删除状态,Session 的特定方法能使对象从一个状态转换到另一个状态。

临时状态(Transient):在使用代理主键的情况下,OID 通常为 null 不处于 Session 的缓存中,在数据库中没有对应的记录。

持久化状态 (也叫”托管”)(Persist):OID 不为 null 位于 Session 缓存中,若在数据库中已经有和其对应的记录,持久化对象和数据库中的相关记录对应 Session 在 flush 缓存时,会根据持久化对象的属性变化,来同步更新数据库。在同一个 Session 实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象。

删除状态(Removed):在数据库中没有和其 OID 对应的记录,不再处于 Session 缓存中,一般情况下,应用程序不该再使用被删除的对象。

游离状态(也叫”脱管”) (Detached):OID 不为 null 不再处于 Session 缓存中。一般情况需下,游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录。

3.对象的基本操作

持久化(保存)

News news = new News("Java", "SUN", new Date());
session.save(news);

Session 的 save() 方法使一个临时对象转变为持久化对象。把对象加入到 Session 缓存中,使它进入持久化状态,选用映射文件指定的标识符生成器,为持久化对象分配唯一的 OID。并计划执行一条 insert 语句

在 flush 缓存的时候 Hibernate 通过持久化对象的 OID 来维持它和数据库相关记录的对应关系。当 News 对象处于持久化状态时。不允许程序随意修改它的 ID

persist() 和 save() 区别:

当对一个 OID 不为 Null 的对象执行 save() 方法时, 会把该对象以一个新的 OID 保存到数据库中; 但执行 persist() 方法时会抛出一个异常.

对象查询(单个)

News news = (News) session.get(News.class, 1);
News news = (News) session.load(News.class, 1);

get 与 load:

1. 执行 get 方法: 会立即加载对象. 执行 load 方法, 若不适用该对象, 则不会立即执行查询操作, 而返回一个代理对象,get 是 立即检索, load 是延迟检索.

2. load 方法可能会抛出 LazyInitializationException 异常: 在需要初始化 代理对象之前已经关闭了 Session

3. 若数据表中没有对应的记录, Session 也没有被关闭. get 返回 null load 若不使用该对象的任何属性, 没问题; 若需要初始化了,抛出异常.

延迟加载指的就是,当完成load操作之后,并不会马上发出sql语句,只有在使用到该对象时才会发出sql 当完成load之后,u其实是一个代理对象,这个代理对象中仅仅只有一个id的值

修改单个对象

News news = (News) session.get(News.class, 1);
news.setAuthor("SUN");
session.update(news);

update 有以下注意事项

1. 若更新一个持久化对象,不需要显示的调用 update 方法。因为在调用 Transaction 的 commit() 方法时,会先执行 session 的 flush 方法。(如上面的第三句没有必要)

2. 更新一个游离对象,需要显式的调用 session 的 update 方法。可以把一个游离对象变为持久化对象。

3. 无论要更新的游离对象和数据表的记录是否一致,都会发送 UPDATE 语句。在 .hbm.xml 文件的 class 节点设置 select-before-update=true (默认为 false) 可以让更新数据时先判断数据是否存在,让 update 方法不再盲目的出发 update 语句。

4. 若数据表中没有对应的记录,但还调用了 update 方法,会抛出异常。

5.当 update() 方法关联一个游离对象时,如果在 Session 的缓存中已经存在相同 OID 的持久化对象,会抛出异常。因为在 Session 缓存中,不能有两个 OID 相同的对象!

6.Session 的 saveOrUpdate()方法同时包含了 save() 与 update() 方法的功能。OID 为空时 save,不为空时 update。

删除对象

News news = (News) session.get(News.class, 163840);
session.delete(news);

delete: 执行删除操作.

只要 OID 和数据表中一条记录对应,就会准备执行 delete 操作 若 OID 在数据表中没有对应的记录,则抛出异常。

可以通过设置 hibernate 配置文件 hibernate.use_identifier_rollback 为 true,使删除对象后, 把其 OID 置为 null。

舍弃修改使用 evict,evict 表示从 session 缓存中把指定的持久化对象移除,使该对象的修改不影响数据库。

News news1 = (News) session.get(News.class, 1);
News news2 = (News) session.get(News.class, 2);
news1.setTitle("AA");
news2.setTitle("BB");
session.evict(news1);

refresh与 flush

语法:

session.refresh(news);
session.flush();

refresh 强制从数据库查询该对象 ,获取数据库最新纪录后设置对象的属性。

flush 强制将修改过后的对象刷新到数据库,并清除一级缓存。

4.常用注解

使用注解取代了原来的 xml 配置,在 hibernate 核心配置文件中注册 mapping class="cn.hx.entity.User",在实体中使用如下注解:

1.类级别注解

@Entity 注解将一个类声明为实体Bean。实体必须有@Id。可以不加 @Table 注解也能生成表,name 为表名。推荐两个注解同时添加,@Entity 与@Table只写一个,会影响 hql,所以建议写全。

@Table(name="tbl_sky")

  • name 表名,可省略
  • schema 模式 
  • catalog 目录

@Table 注解有上述属性,为了兼容各种数据库,hibernate 将数据库分为:数据库系统-->catalog(目录)-->schema(模式) -->表,视图-->字段。但是各数据库厂家对它们的支持不同。MySQL的 schema 表示数据库名, catalog 不支持。Oracle 的 schema 表示用户 ID,catalog 不支持。一般我们开发时只需要增加表名即可。如果没有指明表名,则与实体名相同。

2.属性级别的注解

属性级别的注解可以放在属性上或者 get 方法上。

@Id 注解声明了该实体Bean的ID标识。  

注意:作为主键的字符串必须限制长度,否则执行不报错,数据库没有表

@GeneratedValue(strategy = *)

  • GenerationType.AUTO: 指定主键生成策略 默认 AUTO,hibernate4 中会自动适配,hibernate5 中会自动创建一张表存储主键。

  • GenerationType.IDENTITY:适合于 Mysql 或 sqlserver 等支持自增的 dbms,主键值不由 hibernate 维护。

  • GenerationType.SEQUENCE:适合于 oracle 等支持序列的 dbms,主键值不由 hibernate 维护,由序列产生。 

  • GenerationType.TABLE:使用表来管理主键。

常用的主键策略有,手工赋值的策略,表格策略,数据库 uuid 策略等,配置如下:

@Id
@GeneratedValue(generator = "sid")
@GenericGenerator(name = "sid", strategy = "assigned")

表格策略

@Id
@GeneratedValue(strategy=GenerationType.TABLE, generator="my_table")
@TableGenerator(
    name = "my_table",    
    allocationSize = 1  // 增长值
)

数据库 uuid 策略

@Id
@GenericGenerator(name = "my-uuid", strategy = "uuid")
@GeneratedValue(generator = "my-uuid")

普通字段

@Column(name="name_t")

  • name="columnName";                         列名 
  • boolean unique() default false;           是否在该列上设置唯一约束 
  • boolean nullable() default true;           列可空? 
  • boolean insertable() default true;        该列是否作为生成 insert语句的一个列 
  • boolean updatable() default true;        该列是否作为生成 update语句的一个列 
  • String columnDefinition() default"";      默认值 
  • String table() default "";                       定义对应的表(deault 是主表) 
  • int length() default 255;                       列长度 
  • int precision() default 0; 
  • decimalprecision                                  decimal精度 
  • int scale() default 0; 
  • decimal scale                                      decimal长度

columnDefinition  

用法1:@Column(name = "Email",columnDefinition="varchar(128) not null") 

用法2:@Column(name = "Remark",columnDefinition="text") 大文本格式

@Transient   持久化忽略该字段关联映射注解

案例:

@Entity
@Table(name = "USER")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "user_name",length = 50)
    private String userName;
    @Column(name = "password",length = 50)
    private String password;
    @Transient
    private News news;
}
5.多表级联

双向一对多是最常用的级联关系 @JoinColumn(name="cid") 当前实体表加入外键名为cid mappedBy="classroom" 不由当前属性维系关系,由集合里面的对象的classroom属性维系关系。

@Entity
@Table(name = "tb_classroom")
public class Classroom {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    @OneToMany(mappedBy = "classroom")
    private Set<Student> students;
}

@Entity
@Table(name = "tb_student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String no;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "cid")
    private Classroom classroom;
}

双向一对一用的较少,大部分设置与一对多类似。也是使用 mappedBy 指定由对面该属性维系关系,JoinColumn 增加外键字段。

@Entity
@Table(name = "tb_person")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    @OneToOne(mappedBy = "person")
    private IDCard idCard;
}

@Entity
@Table(name = "tb_idCard")
public class IDCard {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String no;
    @OneToOne
    @JoinColumn(name = "pid")
    private Person person;
}

双向多对多关系,角色与资源(学生与课程关系),重点就是增加中间表。joinColumns 表示当前类的主键在中间表中的名字,inverseJoinColumns 表示对面表的 id 在当前中间表的名字。

@Entity
@Table(name = "tb_role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    @ManyToMany(mappedBy = "roles")
    private Set<Resource> resources;
}

@Entity
@Table(name = "tb_resource")
public class Resource {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    @ManyToMany
    @JoinTable(name = "tb_role_resource", joinColumns = {@JoinColumn(name = "re_id")},
            inverseJoinColumns = {@JoinColumn(name = "ro_id")})
    @LazyCollection(LazyCollectionOption.EXTRA)
    private Set<Role> roles;
}

fetch = FetchType.EAGER

  • LAZY 消极
  • EAGER 积极

@LazyCollection( value = LazyCollectionOption.EXTRA)

  • TRUE (集合具有延迟性,只有在访问的时候才加载)
  • EXTRA (集合具有延迟性,并且所有的操作都会尽量避免加载集合,对于一个巨大的集合特别有用,因为这样的集合中的元素没有必要全部加载)
  • FALSE(非延迟加载的关联)  

6.HQL 查询

HQL 是 Hibernate Query Language 的缩写,提供更加丰富灵活、更为强大的查询能力;HQL 更接近 SQL 语句查询语法。HQL 相当于简化版的面向对象的 SQL。

@Entity
@Table(name="tb_department")
public class Department  {
    @Id
    @GeneratedValue
    private int id;
    private String name;

    @OneToMany(mappedBy="department")
    @LazyCollection(LazyCollectionOption.EXTRA)
    private Set<Employee> employees;
}

@Entity
@Table(name="tb_employee")
public class Employee  {
    @Id
    @GeneratedValue
    private int id;
    private String name;
    private String no;
    private Double salary;
    @ManyToOne(fetch= FetchType.LAZY)//延迟加载
    @JoinColumn(name="cid")//外键
    private Department department;
}

Hibernate 提供了以下几种检索对象的方式

1.基于Query 的参数绑定查询

String hql = "FROM Employee e WHERE e.salary > ?1 AND e.name LIKE ?2 AND e.department = ?3 ORDER BY e.salary";
Query<Employee> query = session.createQuery(hql);
Department dept = new Department();
dept.setId(1);
List<Employee> employees = query
        .setParameter(1, 300.0)
        .setParameter(2, "张%")
        .setParameter(3, dept).list();
System.out.println(employees);

2.基于命名参数的查询

String hql = "FROM Employee e WHERE e.salary > :sal AND e.name LIKE :name";
Query<Employee> query = session.createQuery(hql);
List<Employee> employees = query
        .setParameter("sal", 300.0)
        .setParameter("name", "张%").list();
System.out.println(employees);

3.分页查询

String hql = "FROM Employee";
Query<Employee> query = session.createQuery(hql);
int pageNo = 2;
int pageSize = 2;
List<Employee> employees = query
        .setFirstResult((pageNo - 1) * pageSize)
        .setMaxResults(pageSize).list();
System.out.println(employees);

4.对象查询

String hql = "SELECT e.name, e.salary, e.department FROM Employee e WHERE e.department = :department";
Query query = session.createQuery(hql);
Department department = new Department();
department.setId(1);
List<Object[]> result = query.setParameter("department", department).list();
for (Object[] objects : result) {
    System.out.println(Arrays.toString(objects));
}

5.构造控制查询

String hql = "SELECT new Employee(e.name, e.salary, e.department) FROM Employee e WHERE e.department = :department";
Query<Employee> query = session.createQuery(hql);
Department department = new Department();
department.setId(1);
List<Employee> result = query.setParameter("department", department).list();

6.分组查询

String hql = "SELECT e.department, min(e.salary), max(e.salary) FROM Employee e GROUP BY e.department HAVING min(salary) > :minSal";
Query query = session.createQuery(hql).setParameter("minSal", 200.0);
List<Object[]> result = query.list();
for (Object[] objects : result) {
    System.out.println(Arrays.toString(objects));
}

7.左连接查询

String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN d.employees";
//String hql = "FROM Department d INNER JOIN FETCH d.employees";
List<Department> departments = session.createQuery(hql).list();
// departments = new ArrayList<>(new HashSet<>(departments));
for (Department dept : departments) {
    System.out.println(dept.getName() + "-" + dept.getEmployees().size());
}
7.Criteria 查询

hibernate 增加了 criteria[kraɪ'tɪəriə] 可以添加大量的 Restrictions[rɪsˈtrɪkʃənz] 里面的静态方法,用于各种条件查询。

Criteria criteria = session.createCriteria(Employee.class);
criteria.add(Restrictions.eq("name", "张三"));
criteria.add(Restrictions.ge("salary", 500.0));
Employee employee = (Employee) criteria.uniqueResult();

Criteria and 和 or 查询需要使用另外两个对象,Conjunction 用于添加 and 条件,Disjunction 用于添加 or 条件。

Criteria criteria = session.createCriteria(Employee.class);
Conjunction conjunction = Restrictions.conjunction();
Disjunction disjunction = Restrictions.disjunction();
disjunction.add(Restrictions.ge("salary", 500.0));
disjunction.add(Restrictions.isNull("no"));
criteria.add(disjunction);
List list = criteria.list();
System.out.println(list);

Criteria 统计查询

Criteria criteria = session.createCriteria(Employee.class);
// 统计查询: 使用 Projection 来表示: 可以由 Projections 的静态方法得到
criteria.setProjection(Projections.max("salary"));
System.out.println(criteria.uniqueResult());

Criteria 排序与分页

Criteria criteria = session.createCriteria(Employee.class);
criteria.addOrder(Order.asc("salary"));
criteria.addOrder(Order.desc("name"));
int pageSize = 2;
int pageNo = 1;
List list = criteria.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list();
System.out.println(list);

原生 SQL 执行

String sql = "INSERT INTO tb_department VALUES(?, ?)";
Query query = session.createSQLQuery(sql);
query.setParameter(1, 280).setParameter(2, "hhh").executeUpdate(); 

Restrictions 常见条件如下

=           Restrictions.eq() 等于
<>           Restrictions.not(Exprission.eq()) 不等于
>           Restrictions.gt() 大于
>=           Restrictions.ge() 大于等于
<           Restrictions.lt() 小于
<=           Restrictions.le() 小于等于
is null         Restrictions.isNull() 等于空值
is not null      Restrictions.isNotNull() 非空值
like          Restrictions.like() 字符串模式匹配
and           Restrictions.and() 逻辑与
and          Restrictions.conjunction() 逻辑与 
or           Restrictions.or() 逻辑或
or           Restrictions.disjunction() 逻辑或
not          Restrictions.not() 逻辑非
in(列表)        Restrictions.in() 等于列表中的某一个值
ont in(列表)        Restrictions.not(Restrictions.in())不等于列表中任意一个值
between x and y     Restrictions.between() 闭区间xy中的任意值
not between x and y  Restrictions.not(Restrictions..between()) 小于值X或者大于值y
8.缓存

一级缓存是 Session 级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate 管理,二级缓存是基于 SessionFactory 级别的缓存,它是属于进程范围的缓存,需要手动开启。

导入 hibernate 里面 ehcache 的 jar 包

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.6</version>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
    <version>5.4.10.Final</version>
</dependency>

配置文件里面

<!-- 启用二级缓存 -->
<property name="cache.use_second_level_cache">true</property>
    	
<!-- 配置使用的二级缓存的产品 -->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

实体文件里面

@Entity
@Table(name="gg_employee")
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class Employee {

缓存策略有如下四个,使用时最好将fetch=FetchType.LAZY

  • READ_WRITE:(实体读/写缓存)允许更新,更新后自动同步到缓存。允许新增,新增记录后自动同步到缓存。保证 read committed 隔离级别及可重复读隔离级别(通过时间戳实现)整个过程加锁,如果当前事务的时间戳早于二级缓存中的条目的时间戳,说明该条目已经被别的事务修改了,此时重新查询一次数据库,否则才使用缓存数据,因此保证可重复读隔离级别。读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁。
  • NONSTRICT_READ_WRITE:(实体非严格读/写缓存)允许更新,更新后缓存失效,需再查询一次。允许新增,新增记录自动加到二级缓存中。整个过程不加锁。
  • TRANSACTIONAL:(实体事务缓存)缓存支持事务,发生异常的时候,缓存也能够回滚,只支持 jta 环境。
  • READ_ONLY:(实体只读缓存)只读缓存不允许更新,将报错Can't write to a readonly object。允许新增,(从2.0以后新增直接添加到二级缓存)
Employee employee = (Employee) session.get(Employee.class, 1);
System.out.println(employee.getName());

transaction.commit();
session.close();
session = sessionFactory.openSession();
transaction = session.beginTransaction();

Employee employee2 = (Employee) session.get(Employee.class, 1);
System.out.println(employee2.getName());

Query 与 Criteria 查询的缓存需要设置

Query query = session.createQuery("FROM Employee");
query.setCacheable(true);

Criteria criteria = session.createCriteria(Employee.class);
criteria.setCacheable(true);
9.调用存储过程

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。

session.doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException {
    // 通过 JDBC 原生的 API 进行操作, 效率最高, 速度最快!
    CallableStatement cStmt = connection.prepareCall("{call demoSp(?, ?)}");
    cStmt.setInt(1, 0);
    cStmt.registerOutParameter(2, Types.INTEGER);
    cStmt.execute();
    System.out.println(cStmt.getString(2));
    }
});

创建存储过程

CREATE 
PROCEDURE `hibernate`.`demoSp` (IN sex_id INT, OUT user_count INT) 
BEGIN
  IF sex_id = 0 
  THEN 
  SELECT 
    COUNT(*) 
  FROM
    hibernate.gg_employee
  WHERE gg_employee.name = '张三' INTO user_count ;
  ELSE 
  SELECT 
    COUNT(*) 
  FROM
    hibernate.gg_employee
  WHERE gg_employee.name = '李四' INTO user_count ;
  END IF ;
END

调用存储过程

SET @user_count = 0;
CALL `hibernate`.`demoSp`(0, @user_count);
SELECT @user_count;
10.锁机制

Hibernate提供了乐观锁和悲观锁机制,主要用于解决事务并发问题。 

悲观锁:Hibernate认为任何操作都可能发生并发,因此在第一个线程查询数据时,就把该条记录锁住。此时其他线程对该记录不能做任何操作(即增删改操作都不能)。必须等当前线程事务结束才可以进行操作。 

悲观锁的实现原理 Hibernate悲观锁机制实际上是采用数据库的锁机制实现。 数据库中SQL语句最后加for update则把记录直接锁死,其他用户增删改查都不行,只能等待:select * from TRAIN where id=1 for update; Hibernate中load重载方法:

session.load(Train.class,1,LockOptions.UPGRADE);

只能等待当前用户提交或回滚,若等待超时则报异常! 悲观锁的缺点:处理效率很低。

悲观锁使用步骤及测试 

CREATE TABLE TRAIN(
ID NUMBER PRIMARY KEY,	T_START VARCHAR2(20),
T_END VARCHAR2(20),T_TICKET NUMBER);

INSERT INTO TRAIN VALUES(1,'beijing','shanghai',100);

创建实体

@Entity
public class Train {
    @GeneratedValue
    @Id
    private int id;
    private String name;
    private int ticket;

创建两个线程

public class ThreadClient extends Thread {//继承Thread
    public void run(){//模拟购票操作
        Session session=HibernateUtil.getSession();
        Transaction tx=session.beginTransaction();
        //Train train=(Train)session.load(Train.class, 1);//不加锁时,查询出火车信息
        Train train=(Train)session.load(Train.class,1,LockMode.UPGRADE);//设置悲观锁
        if(train.getTicket()>=1){//判断剩余票数>=购买票数
            try {//满足购票条件,进行购票操作
                Thread.sleep(2000);//模拟用户操作
            } catch (InterruptedException e) { 	e.printStackTrace(); 	}
			
            int ticket=train.getTicket()-1;//将票数更新
            train.setTicket(ticket);		
            System.out.println("购票成功!");
        }else {  System.out.println("票数不足,购买失败!");	}
        tx.commit();//持久对象,提交就自动同步,相当于执行了update    session.close();  }
}

测试类

public static void main(String[] args) {
	ThreadClient c1=new ThreadClient();		c1.start();
	ThreadClient c2=new ThreadClient();		c2.start();
 /** 若ThreadClient类为无锁查询,则显示2个购买成功,但数据库却减少1张票。若ThreadClient类加了悲观锁,则显示2个购买成功,数据库减少2张票。**/}

乐观锁:认为发生并发几率非常小。相同的记录不同的用户都可以查询访问,当多个人都要修改该记录时,只有第一个提交的用户成功,其他的会抛出异常,提示失败!

乐观锁的实现原理

乐观锁机制是借助于一个“版本”字段实现,当第一个更新用户提交成功后,Hibernate会自动将该“版本”字段值+1,当其他用户提交,如果版本字段小于数据库中的值,则会抛出异常,提示失败。如果不使用框架技术,那么我们需要手工做对比,使用Hibernate框架后,Hibernate可以帮助我们做version对比的操作。

乐观锁使用步骤及测试 step1:向TRAIN表中添加一个VERSION版本字段 

@Entity
public class Train {
    @GeneratedValue
    @Id
    private int id;
    private String name;
    private int ticket;
    @Version
    private int version;

将悲观锁中的ThreadClient类,改为不加锁的load方法

Train train=(Train)session.load(Train.class, 1);

执行结果:先提交的事务执行成功,后提交的事务执行失败,报错信息为: 

StaleObjectStateException: Row was updated or deleted by another transaction 

11.与 spring 整合

依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.48</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.10.Final</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.12.RELEASE</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

db.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/sssp
jdbc.username=root
jdbc.password=root

spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 配置自动扫描的包 -->
    <context:component-scan base-package="cn.hx"/>

    <!-- 配置数据源 -->
    <context:property-placeholder location="classpath:db.properties"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 设置Spring取那个包中查找相应的实体类 -->
        <property name="packagesToScan" value="cn.hx.entity"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect.storage_engine">innodb</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <!-- 配置事务管理器 -->
    <bean name="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
</beans>

service

@Service
public class UserService {
    @Resource
    private UserDao userDao;

    @Transactional
    public void add(User user) {
        userDao.add(user);
    }

    public List<User> findAll() {
        return userDao.findAll();
    }
}

通用 dao

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;


public class BaseDao<T> {

    @Autowired
    private SessionFactory sessionFactory;

    private Class<T> clz;

    public Class<T> getClz() {
        if (clz == null) { // 获取泛型的Class对象
            clz = ((Class<T>) (((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments()[0]));
        }
        return clz;
    }

    protected Session getCurrentSession() {
        return this.sessionFactory.getCurrentSession();
    }

    public Serializable save(T o) {
        return this.getCurrentSession().save(o);
    }

    public T get(Serializable id) {
        return (T) this.getCurrentSession().get(clz, id);
    }

    public T get(String hql) {
        Query q = this.getCurrentSession().createQuery(hql);
        List<T> l = q.list();
        if (l != null && l.size() > 0) {
            return l.get(0);
        }
        return null;
    }

    public T get(String hql, Map<String, Object> params) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        List<T> l = q.list();
        if (l != null && l.size() > 0) {
            return l.get(0);
        }
        return null;
    }

    public void delete(T o) {
        this.getCurrentSession().delete(o);
    }

    public void update(T o) {
        this.getCurrentSession().update(o);
    }

    public void saveOrUpdate(T o) {
        this.getCurrentSession().saveOrUpdate(o);
    }

    public List<T> find(String hql) {
        Query q = this.getCurrentSession().createQuery(hql);
        return q.list();
    }

    public List<T> find(String hql, Map<String, Object> params) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        return q.list();
    }

    public List<T> find(String hql, Map<String, Object> params, int page, int rows) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
    }

    public List<T> find(String hql, int page, int rows) {
        Query q = this.getCurrentSession().createQuery(hql);
        return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
    }

    public Long count(String hql) {
        Query q = this.getCurrentSession().createQuery(hql);
        return (Long) q.uniqueResult();
    }

    public Long count(String hql, Map<String, Object> params) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        return (Long) q.uniqueResult();
    }

    public int executeHql(String hql) {
        Query q = this.getCurrentSession().createQuery(hql);
        return q.executeUpdate();
    }

    public int executeHql(String hql, Map<String, Object> params) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        return q.executeUpdate();
    }

}

当然也可以使用继承 HibernateDaoSupport 就能获取 HibernateDaoSupport 里面的方法完成增删改查。

@Repository
public class UserDao extends HibernateDaoSupport {

    @Resource
    public void setSuperSessionFactory(SessionFactory sessionFactory) {
        this.setSessionFactory(sessionFactory);
    }

    public void add(User user) {
        getHibernateTemplate().save(user);
    }

    public List<User> findAll() {
        return getHibernateTemplate().findByExample(new User());
    }
}

sessionFactory.getCurrentSession();
1. openSession 从字面上可以看得出来,是打开一个新的 session 对象,而且每次使用都是打开一个新的 session,假如连续使用多次,则获得的 session 不是同一个对象,并且使用完需要调用 close 方法关闭 session。
2. getCurrentSession ,从字面上可以看得出来,是获取当前上下文一个 session 对象,当第一次使用此方法时,会自动产生一个 session 对象,并且连续使用多次时,得到的 session 都是同一个对象,这就是与 openSession 的区别之一,简单而言,getCurrentSession 就是:如果有已经使用的,用旧的,如果没有,建新的。
注意 :在实际开发中,往往使用 getCurrentSession 多,因为一般是处理同一个事务(即是使用一个数据库的情况),所以在一般情况下比较少使用 openSession 或者说 openSession 是比较老旧的一套接口了;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值