Hibernate架构图:
Hibernate 使用不同的现存 Java API,比如 JDBC,Java 事务 API(JTA),以及 Java 命名和目录界面(JNDI)。JDBC 提供了一个基本的抽象级别的通用关系数据库的功能, Hibernate 支持几乎所有带有 JDBC 驱动的数据库。JNDI 和 JTA 允许 Hibernate 与 J2EE 应用程序服务器相集成。
配置对象是你在任何 Hibernate 应用程序中创造的第一个 Hibernate 对象,并且经常只在应用程序初始化期间创造。它代表了 Hibernate 所需一个配置或属性文件。配置对象提供了两种基础组件。
数据库连接:由 Hibernate 支持的一个或多个配置文件处理。这些文件是 hibernate.properties 和 hibernate.cfg.xml。类映射设置:这个组件创造了 Java 类和数据库表格之间的联系。配置对象是你在任何 Hibernate 应用程序中创造的第一个 Hibernate 对象,并且经常只在应用程序初始化期间创造。它代表了 Hibernate 所需一个配置或属性文件。配置对象提供了两种基础组件。
数据库连接:由 Hibernate 支持的一个或多个配置文件处理。这些文件是 hibernate.properties 和 hibernate.cfg.xml。类映射设置:这个组件创造了 Java 类和数据库表格之间的联系。配置对象是你在任何 Hibernate 应用程序中创造的第一个 Hibernate 对象,并且经常只在应用程序初始化期间创造。它代表了 Hibernate 所需一个配置或属性文件。配置对象提供了两种基础组件。
数据库连接:由 Hibernate 支持的一个或多个配置文件处理。这些文件是 hibernate.properties 和 hibernate.cfg.xml。类映射设置:这个组件创造了 Java 类和数据库表格之间的联系。配置对象是你在任何 Hibernate 应用程序中创造的第一个 Hibernate 对象,并且经常只在应用程序初始化期间创造。它代表了 Hibernate 所需一个配置或属性文件。配置对象提供了两种基础组件。
数据库连接:由 Hibernate 支持的一个或多个配置文件处理。这些文件是 hibernate.properties 和 hibernate.cfg.xml。类映射设置:这个组件创造了 Java 类和数据库表格之间的联系。Query 对象使用 SQL 或者 Hibernate 查询语言(HQL)字符串在数据库中来检索数据并创造对象。一个查询的实例被用于连结查询参数,限制由查询返回的结果数量,并最终执行查询。
Criteria 对象被用于创造和执行面向规则查询的对象来检索对象。可以利用该类来完成非sql语句的数据过滤查询。
在Hibernate官网http://hibernate.org/orm/releases/下载合适版本的资料,其中包括hibernate的源码、编译好的jar包和文档信息。
对于普通使用只需要把lib\required目录下的jar包引入项目,对于缓存的使用,需要引入lib\optional\ehcache目录下的EHCache的jar包。
hibernate包
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.4.21.Final</version> </dependency>缓存Ehcache
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>5.4.21.Final</version> </dependency>Hibernate6目前还处于测试版。
使用之前还需要引入对应自己DataBase的JDBC驱动。
hibernate.cfg.xml是hibernate配置文件的默认文件名,只需要将hibernate.cfg.xml文件放在项目目录下,hibernate将自动读取文件中的配置。(对于普通的java项目,hibernate.cfg.xml应该放在src目录下)
通常需要配置SQL方言、连接驱动、数据库url地址、用户名和密码等。可以使用mapping标签来指示映射文件路径,可以同hibernate的配置文件放在项目根目录下。
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <!-- 配置一个session工厂--> <session-factory> <!-- sql方言--> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <!-- jdbc驱动--> <property name="hibernate.connection.driver_class"> com.mysql.cj.jdbc.Driver </property> <!-- 数据库链接--> <property name="hibernate.connection.url"> jdbc:mysql://localhost:3306/hibernate_study?serverTimezone=UTC </property> <!-- 用户名--> <property name="hibernate.connection.username"> dev </property> <!-- 密码--> <property name="hibernate.connection.password"> 123456 </property> <!-- 映射文件--> <!-- 支持package、class、file、jar --> <mapping resource="User.hbm.xml" /> </session-factory> </hibernate-configuration>所有的Entity应该是按照java bean的要求编写的POJO对象,以User Entity为例。
User Entity:
package cn.llzero.entity; import java.io.Serializable; public class User implements Serializable { private Integer id; private String name; private String pass; private Character sex; public Integer getId() { return id; } public User setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public User setName(String name) { this.name = name; return this; } public String getPass() { return pass; } public User setPass(String pass) { this.pass = pass; return this; } public Character getSex() { return sex; } public User setSex(Character sex) { this.sex = sex; return this; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pass='" + pass + '\'' + ", sex=" + sex + '}'; } }XML映射文件(User.hbm.xml):
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.llzero.entity.User" table="user"> <meta attribute="description"> User映射,学习Hibernate </meta> <id name="id" column="id" type="integer" > <generator class="native" /> </id> <property name="name" column="name" unique="true" type="string" /> <property name="pass" column="pass" type="string"/> <property name="sex" column="sex" type="char"/> </class> </hibernate-mapping>name => 对应实体的属性, column => 数据库字段, type指示了数据的类型,该属性建议使用hibernate的预设值。
当你准备一个 Hibernate 映射文件时,我们已经看到你把 Java 数据类型映射到了 RDBMS 数据格式。在映射文件中已经声明被使用的 types 不是 Java 数据类型;它们也不是 SQL 数据库类型。这种类型被称为 Hibernate 映射类型,可以从 Java 翻译成 SQL,反之亦然。
在这一章中列举出所有的基础,日期和时间,大型数据对象,和其它内嵌的映射数据类型。
对应的sql:
create table user ( id int auto_increment primary key, name varchar(20) not null, pass varchar(30) not null, sex char default 'M' not null, constraint name unique (name) )charset = utf8;使用hibernate的注解比使用xml映射文件的方式方便,而且具有良好的可观性。特别是在spring推崇的去文档化,尽量减少对xml这类文档的依赖,采用编程的方式解决配置等问题。
@Entity用于对POJO实体类注解,以明确这是一个实体类。
@Entity public class User implements Serializable { ... }@Table用于注解实体对应的表。
@Entity @Table(name = "user") public class User implements Serializable { ... }@Id用于对primark key主键属性的注解。
@Entity @Table(name = "user") public class User implements Serializable { @Id private Integer id; ... }@GeneratedValue注解指示了属性值的生成策略,通常选用GenerationType.AUTO即可。
@Entity @Table(name = "user") public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; ... }GenerationType.AUTO : 自动选择
GenerationType.SEQUENCE : 按顺序自增
GenerationType.Table : 按值表的方式,由hibernate生成一个主键表
GenerationType.IDENTITY : 保持与entity一致
@Column注解用于映射实体的属性和表的字段的关系。
@Entity @Table(name = "user") public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Integer id; @Column(name = "name", nullable = false, unique = true) private String name; ... }指示在对象的更新与插入时,过滤null值的属性。
@Entity @Table(name = "user") @DynamicUpdate @DynamicInsert public class User implements Serializable { ... }对于关系模式的表间关系有一对一、一对多、多对多的关系,可以通过注解的方式来完成实体间的关系。
注意:在完成对象关系时,有关系“提供方”和“使用方”。关系的提供方要维护两者关系,负责对数据的操作。
对于一对一和多对多,维护方按照实际对象关系确定即可,对于一对多的方式,维护方只能为一的那个对象。
现在有User和Role两个类,假设一个用户只能有一个角色,一个角色只能属于一个用户。
对于User类:
@Entity @Table(name = "user") @DynamicUpdate @DynamicInsert public class User implements Serializable { ... @OneToOne @JoinColumn(name = "role_id", referencedColumnName = "id", nullable = false) private Role role; ... }对于Role类:
@Entity @Table(name = "role") @DynamicInsert @DynamicUpdate public class Role implements Serializable { ... @OneToOne(mappedBy = "role") private User user; ... }假设一个用户有多个角色,一个角色只能属于一个用户。
对于User类:
@Entity @Table(name = "user") @DynamicUpdate @DynamicInsert public class User implements Serializable { ... @OneToMany(mappedBy = "user") private Set<Role> roles = new HashSet<>(); ... }对于Role类:
@Entity @Table(name = "role") @DynamicInsert @DynamicUpdate public class Role implements Serializable { ... @ManyToOne @JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false) private User user; ... }假设一个用户可以有多个角色 ,一个角色可以属于多个用户。(注意:多对多的情况需要准备中间表。)
根据实际分析,由User来作关系的维护者。
对于User类:
@Entity @Table(name = "user") @DynamicUpdate @DynamicInsert public class User implements Serializable { ... // @JoinColumns定义了中间表的信息 @ManyToMany(targetEntity = Role.class) @JoinTable(name = "user_role", joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)} , inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id", nullable = false)}) private Set<Role> roles = new HashSet<>(); ... }对于Role类:
@Entity @Table(name = "role") @DynamicInsert @DynamicUpdate public class Role implements Serializable { ... @ManyToMany(mappedBy = "roles") private Set<User> users = new HashSet<>(); ... }创建会话首先需要SessionFactory,可以通过Configuration读取hibernate的配置文件,或者通过setProperty的方式设定hibernate的属性。
读取项目目录下的默认配置文件(hibernate.cfg.xml)
// 以下语句将使用默认的hibernate.cfg.xml配置文件 private final static SessionFactory FACOTRY = new Configuration().configure().buildSessionFactory();指定配置文件
// 指定配置文件,只需要在invoke configure方法时传入配置文件信息 // configure方法原型如下: configure(); // 读取hibernate.cfg.xml // 以下自定义配置文件名 configure(String resource); configure(URL url); configure(File configFile);通过编程方式设定 setProperty方法:
// 一个简单的例子如下: new Configuration().configure() .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect") .setProperty("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver") .setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/hibernate_study?serverTimezone=UTC") .setProperty("hibernate.connection.username", "dev").setProperty("hibernate.connection.password", "123456") .buildSessionFactory();setProperties方法:
// 实例化Properties对象 Properties prs = new Properties(); prs.put("hibernate.dialect", "org.hibernate.dialect.MysqlDialect"); prs.put("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver"); ... // 设置配置 new Configuration().configure().setProperties(prs);当获得SeesionFactory后,就可以通过factory来开启Session会话。
// 开户一个会话 Session session = FACTORY.openSession(); coding... // 使用完成后关闭会话 session.close(); // 也可以通过factory关闭所有已打开的会话,这是不安全的 //FACTORY.close();HQL的语法和T-SQL的语法大致相同,不过HQL是对对象进行操作(对象已经和相应的表进行映射 ),而T-SQL直接针对的是表。使用HQL的INSERT语句时需要注意,INSERT语句不允许直接插入新的对象数据,但是可以将一个对象的信息插入另一个对象(与SELECT语句结合使用)。
查询单个属性或者多个属性时,必要的时候需要将结果存入map中,方便对数据进行读取。
// 只查询id属性 String hql = "SELECT id FROM User"; Query query = Session.createQuery(hql); // 获取结果集 List list = query.list(); // 返回的是含有属性值的列表 // 可以转换为map List list1 = query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP).list(); // 返回含有map的list列表 // 对于多个属性的查询,以上的转换是非常必要的,否则只能得到Object对象 // 注意:hibernate的很多方法已经弃用,包括setResultTransformer方法。 // 实际上对于简单的CRUD,可以通过hibernate封装的find、save、saveOrUpdate、delete方法来操作实体。 String hql = "SELECT id, name FROM User"; Query query1 = session.createQuery(hql); List list2 = query1.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP).list();曾几何时,使用jdbc的时候,使用"?“作为占位符。现在使用hibernate绑定参数变得更加方便,可以使用”:“的方式,或者使用jpa的占位格式,”?"。
// 绑定参数 String hql = "UPDATE User Set name = :name, pass = :pass WHERE id = :id"; // 对于非查询操作,记得开启事务 Transaction tx = sexxion.beginTransaction(); Query query = session.createQuery(hql, User.class); query.setParameter("name", "newName"); query.setParameter("pass", "newPass"); query.setParameter("id", 1); System.out.println(query.executeUpdate()); // 提交当前事务 tx.commit(); // 使用jpa的占位符格式 // jpa的格式的问号加上参数索引的方式 // 即 "?<parameter-index>" // 注意:索引下标是从0开始的 String hql = "FROM User WHERE id = ?0"; Query query = session.createQuery(hql, User.class); query.setParameter(0, 1); // fistr param is index System.out.println(query.list());在实际的测试中,对于非查询的语句需要开启事务,否则将会出现异常。其次与查询不同的是需要调用==executeUpdate()==方法来执行非查询操作。
String hql = "DELETE FROM User WHERE id = 1"; // 开启事务 Transaction tx = session.beginTransaction(); try { // 执行delete int res = session.createQuery(hql).executeUpdate(); // 提交事务 tx.commit(); }catch (HibernateException e) { e.printStackTrace(); tx.rollback(); // 回滚 }finally { // 关闭session session.close(); } return -1;WHERE子句能够对查询的数据进行filter,支持使用AND和OR关键字来组合逻辑,其中表达式符号有"="、"!="、">"、">="、"<"、"<=“以及”()"等。
String hql = "FROM User WHERE id = 1 OR id = 3 OR id >= 10";AS可以为表或者某一属性取别名。
String hql = "SELECT U.id AS uid, U.name AS uname FROM User AS U";对于排序,同T-SQL一样,使用ORDER BY关键字,多个参与排序的属性使用“,”隔开。
DESC : 降序
ASC (默认): 升序
String hql = "FROM User ORDER BY name, id DESC";hql的分组同T-SQL一致,多个参与分组的属性采用半角逗号隔开。
String hql = "FROM User GROUP BY name, pass";hibernate的分页查询通过调用两个方法来实现,其效果和LIMIT关键字相同。
setFirstResult(int resultPoint) : 开始读取的位置
setMaxResults(int num) : 每次最大多少条数据
String hql = "FROM User"; // 以下语句从第四个对象开始读取,最多返回5个对象 Query query = session.createQuery(hql, User.class).setFirstResult(3).setMaxRusults(5).list();hibernate的标查询使用到的Class是Criteria,其实质是将hql进行分拆再组合。
Criteria cr = session.createCriteria(User.class); System.out.println(cr.list()); // 对标准的限制 // 使用add方法来添加对标准的限制 // Restrictions中定义相关限制 cr.add(Restrictions.eq("id", 3)); System.out.println(cr.list()); cr.add(Restrictions.isNull("id")); cr.add(Restrictions.isNotNull("id")); cr.add(Restrictions.isEmpty("id"); cr.add(Restrictions.isNotEmpty("id")); cr.add(Restrictions.ge("id", 10)); cr.add(Restrictions.le("id", 20)); cr.add(Restrictions.gt("id", 30)); cr.add(Restrictions.lt("id", 40))); // 模糊查询,使用"%"或"_" cr.add(Restrictions.like("id", "5%")); // 值区间 cr.add(Restrictions.between("id", 2, 5)); // AND或OR条件 Criterion cr1 = Restrictions.eq("id", 3); Criterion cr2 = Restrictions.eq("pass", "123456"); System.out.println(cr.add(Restrictions.and(cr1, cr2)).list()); System.out.println(cr.add(Restrictions.or(cr1, cr2)).list()); // 分页 System.out.println(cr.setFirstResult(2).setMaxResults(2).list()); // 排序,使用Order类 // 注意addOrder方法 System.out.println(cr.addOrder(Order.desc("id")).list()); // 预测与聚合 // Projections类中的方法有求最值、平均值等的方法 System.out.println(cr.setProjection(Projections.avg("id"))); System.out.println(cr.setProjection(Projections.max("id")).list());hibernate除了支持HQL与标准查询以外,同样也可以使用原生的SQL语句进行查询。但是并不建议那么做,因为不同的DBMS的语法存在出入,可能会为移植带来不便。
String sql = "SELECT * FROM user"; NativeQuery sqlQuery = session.createSQLQuery(sql); System.out.println(sqlQuery.list()); // 标量查询 String sql = "SELECT id,name FROM user"; NativeQuery sqlQuery = session.createSQLQuery(sql); // 把结果转换为map对象 Query query = sqlQuery.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); System.out.println(query.list()); // 实体查询 // 通过SqlQuery的addEntity(O)方法添加实体映射数据 String sql = "SELECT * FROM user"; SQLQuery sqlQuery = session.createSQLQuery(sql); // 绑定的实体 sqlQuery.addEntity("cn.llzero.entity.User"); sqlQuery.addEntity(User.class); System.out.println(sqlQuery.list()); // 动态参数 // 可以参考HQL String sql = "SELECT * FROM user WHERE id = :id"; SQLQuery sqlQuery = session.createSQLQuery(sql); sqlQuery.addEntity("cn.llzero.entity.User"); sqlQuery.setParameter("id", 3); System.out.println(sqlQuery.getSingleResult());实现hibernate的拦截器有两种方式,一种是实现Interceptor接口,还有一种就是继承EmptyInterceptor空拦截器选择性的重写自己想要实现的方法。
Interceptor的定义如下:
public interface Interceptor { boolean onLoad(Object var1, Serializable var2, Object[] var3, String[] var4, Type[] var5) throws CallbackException; boolean onFlushDirty(Object var1, Serializable var2, Object[] var3, Object[] var4, String[] var5, Type[] var6) throws CallbackException; boolean onSave(Object var1, Serializable var2, Object[] var3, String[] var4, Type[] var5) throws CallbackException; void onDelete(Object var1, Serializable var2, Object[] var3, String[] var4, Type[] var5) throws CallbackException; void onCollectionRecreate(Object var1, Serializable var2) throws CallbackException; void onCollectionRemove(Object var1, Serializable var2) throws CallbackException; void onCollectionUpdate(Object var1, Serializable var2) throws CallbackException; void preFlush(Iterator var1) throws CallbackException; void postFlush(Iterator var1) throws CallbackException; Boolean isTransient(Object var1); int[] findDirty(Object var1, Serializable var2, Object[] var3, Object[] var4, String[] var5, Type[] var6); Object instantiate(String var1, EntityMode var2, Serializable var3) throws CallbackException; String getEntityName(Object var1) throws CallbackException; Object getEntity(String var1, Serializable var2) throws CallbackException; void afterTransactionBegin(Transaction var1); void beforeTransactionCompletion(Transaction var1); void afterTransactionCompletion(Transaction var1); /** @deprecated */ @Deprecated String onPrepareStatement(String var1); }编写HibernateInterceptor类,并继承EmptyInterceptor重写方法。
public class HibernateInterceptor extends EmptyInterceptor { @Override public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { System.out.println("on delete!"); super.onDelete(entity, id, state, propertyNames, types); } @Override public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { System.out.println("on flush dirty"); return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types); } @Override public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { System.out.println("on load"); return super.onLoad(entity, id, state, propertyNames, types); } @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { System.out.println("on save"); return super.onSave(entity, id, state, propertyNames, types); } @Override public void postFlush(Iterator entities) { System.out.println("post flush"); super.postFlush(entities); } @Override public void preFlush(Iterator entities) { System.out.println("pre flush"); super.preFlush(entities); } @Override public Boolean isTransient(Object entity) { System.out.println("isTransient"); return super.isTransient(entity); } @Override public Object instantiate(String entityName, EntityMode entityMode, Serializable id) { System.out.println("instantiate"); return super.instantiate(entityName, entityMode, id); } @Override public int[] findDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { System.out.println("findDirty"); return super.findDirty(entity, id, currentState, previousState, propertyNames, types); } @Override public String getEntityName(Object object) { System.out.println("getEntityName"); return super.getEntityName(object); } @Override public Object getEntity(String entityName, Serializable id) { System.out.println("getEntity"); return super.getEntity(entityName, id); } @Override public void afterTransactionBegin(Transaction tx) { System.out.println("afterTransactionBegin"); super.afterTransactionBegin(tx); } @Override public void afterTransactionCompletion(Transaction tx) { System.out.println("afterTransactionCompletion"); super.afterTransactionCompletion(tx); } @Override public void beforeTransactionCompletion(Transaction tx) { System.out.println("beforeTransactionCompletion"); super.beforeTransactionCompletion(tx); } @Override public String onPrepareStatement(String sql) { System.out.println("onPrepareStatement"); return super.onPrepareStatement(sql); } @Override public void onCollectionRemove(Object collection, Serializable key) throws CallbackException { System.out.println("onCollectionRemove"); super.onCollectionRemove(collection, key); } @Override public void onCollectionRecreate(Object collection, Serializable key) throws CallbackException { System.out.println("onCollectionRecreate"); super.onCollectionRecreate(collection, key); } @Override public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException { System.out.println("onCollectionUpdate"); super.onCollectionUpdate(collection, key); } }findDirty() 这个方法在当 flush() 方法在一个 Session 对象上被调用时被调用。 instantiate() 这个方法在一个持续的类被实例化时被调用。 isUnsaved() 这个方法在当一个对象被传到 saveOrUpdate() 方法时被调用。 onDelete() 这个方法在一个对象被删除前被调用。 onFlushDirty() 这个方法在当 Hibernate 探测到一个对象在一次 flush(例如,更新操作)中是脏的(例如,被修改)时被调用。 onLoad() 这个方法在一个对象被初始化之前被调用。 onSave() 这个方法在一个对象被保存前被调用。 postFlush() 这个方法在一次 flush 已经发生并且一个对象已经在内存中被更新后被调用。 preFlush() 这个方法在一次 flush 前被调用。
使用 session.flush() 和 session.clear() 可以同步缓存和清理内存中的缓存,hibernate默认使用的CacheProvider是EHCache。