本文包括以下五个部分:
- ORM思想以及JAP的概述。
- Hibernate框架的简介。
- 模拟Hibernate框架,理解运行原理。
- Hibernate4框架的开发步骤。
- Hibernate的执行过程。
- Hibernate核心API详解。
- Hibernate配置详解。
一、ORM思想以及JAP的概述
1.1 ORM思想的概念
对象-关系映射(Object Relational Mapping,简称ORM),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系型数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。
有众多厂商和开源社区都对ORM思想进行了实现,开发出很多相关的产品。其中包括Hibernate框架,IBatis框架,TopLink框架等。
1.2 ORM框架的优点
对ORM思想进行实现的,从对象数据到关系数据的框架,就称之为ORM框架。
ORM框架具有以下优点:
- 完全面向对象的编程思想,无SQL出现。
减少代码编写,提高工作效率。
- 提高访问数据库的性能,降低访问数据库的频率。
- 具有独立性,发生变化时,不会影响上层的实现。
1.3 JPA的概述
JPA是Sun 公司,制订了一个 规范。底层可使用任意的 ORM 框架作为实现,如果编程时面向 JPA 编程,程序将可以在任意的 ORM 技术之间自由切换。也就是给ORM思想的实现技术制定了一个标准,使得ORM的实现产品具有更强的通用性。
1.4 ORM和JPA的关系
二、Hibernate框架的简介
1.1 Hibernate的概念
Hibernate是基于ORM思想的,对jdbc进行封装的持久层框架!是一个面向java环境的对象-关系数据库映射框架。
1.2 Hibernate的优点
Hibernate有以下优点:
开源免费的持久层框架。
是ORM(Object/RelationalMapping)实现之一,建立面向对象的域和关系之间的映射。
对JDBC进行封装,负责java对象的持久化(CURD操作)。
在分层结构中处于持久化层,封装对数据库的访问细节,使业务逻辑层更专注于实现业务逻辑。
三、模拟Hibernate框架,理解运行原理
3.1 功能需求
在DAO层,设计一个通用的Session类,包含一个通用save()保存方法,任意传入一个带有数据的对象就可以保存到指定的表中。
Hibernate的项目结构如下:
3.2 实现步骤
a. 在数据库设计一个学生表(Student),用来保存学生对象数据。
CREATE TABLE student( id INT PRIMARY KEY, NAME VARCHAR(20), gender VARCHAR(2), age INT );
b. 创建实体类
Student.java
package edu.scut.entity; public class Student { private int id; private String name; private String gender; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
c. 建立Student实体类的映射文件orm.xml
<?xml version="1.0" encoding="UTF-8"?> <mapping> <!-- 类的映射 --> <class name="edu.scut.entity.Student" table="student"> <!-- 属性映射 --> <property name="id" column="id"/> <property name="name" column="name"/> <property name="gender" column="gender"/> <property name="age" column="age"/> </class> </mapping>d. 编写Student的DAO类,用来保存学生数据
StudentDao.java
package edu.scut.dao; import edu.scut.entity.Student; import edu.scut.framework.Session; public class StudentDao { //保存学生 public void saveStudent(Student student){ Session session = new Session(); session.save(student); } }e. 设计通用的session类,即自定义框架framework。读取学生类的映射信息,给对象进行持久化操作。
Session.javapackage edu.scut.framework; import java.lang.reflect.Field; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.dbutils.QueryRunner; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.jaxen.function.IdFunction; import edu.scut.util.C3P0Util; //目的:抽取dao层的形成通用的代码 public class Session { //创建Map集合用于保存所有的calss对象 private static Map<String, ClassMapping> classMappings; //读取orm.xml文件 static{ //读取一次配置文件即可 try { SAXReader reader = new SAXReader(); Document doc = reader.read(Session.class.getResourceAsStream("/orm.xml")); //读取所有的class标签 List<Element> classElements = doc.selectNodes("//class"); if(classElements!=null){ classMappings = new HashMap<String, ClassMapping>(); } //遍历classElements集合 for (Element classElement : classElements) { //创建classMapping对象 ClassMapping cm = new ClassMapping(); //获取每一个class的属性 String name = classElement.attributeValue("name"); String table = classElement.attributeValue("table"); cm.setName(name); cm.setTable(table); //选择其下面的所有property标签 List<Element> propertyElements = classElement.elements("property"); //遍历集合 for (Element pe : propertyElements) { //创建propertyMapping对象 PropertyMapping pm = new PropertyMapping(); String proName = pe.attributeValue("name"); String proColumn = pe.attributeValue("column"); pm.setName(proName); pm.setColumn(proColumn); //将封装好的PropertyMapping对象放进classMapping中 cm.getPropertyMappings().put(pe.attributeValue("name"), pm); } //把封装好的classMapping对象放入classMappings集合当中 classMappings.put(name, cm); } } catch (DocumentException e) { System.out.println("orm.xml文件读取失败!"); e.printStackTrace(); } } QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); //用于保存sql语句 String sql = null; //用于保存需要 赋值的参数 Object[] values = null; /** * 保存方法 * @param object */ public void save(Object object){ try { //准备sql语句和参数 buildSqlAndValues(object); //执行sql语句 qr.update(sql,values); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } //创建sql语句和参数 //String sql = "insert into t_student(id,name,gender,age) values(?,?,?,?)"; private void buildSqlAndValues(Object object) throws Exception, NoSuchFieldException { /**拼接sql语句*/ //前半段 String sql1 = "insert into "; //后半段 String sql2 = "values("; //获取类的名称 String className = object.getClass().getName(); //获取class标签的内容 ClassMapping cm = classMappings.get(className); //根据classMapping获取table的名称 sql1+=cm.getTable()+"("; //拼接字段名称 Map<String, PropertyMapping> pms = cm.getPropertyMappings(); //遍历属性集合 for (Entry<String,PropertyMapping> entry : pms.entrySet()) { //获取字段名称 String column = entry.getValue().getColumn(); sql1+=column+","; //给sql2拼接? sql2+="?,"; } //去掉最后一个逗号 sql1 = sql1.substring(0,sql1.length()-1)+") "; //去掉最后一个问号 sql2 = sql2.substring(0,sql2.length()-1)+") "; //拼接成最终的sql语句 sql = sql1 + sql2; System.out.println(sql); /**获取当前对象的参数*/ //初始化对象数组 values = new Object[pms.size()]; int i=0; //属性名称 for (Entry<String,PropertyMapping> entry : pms.entrySet()) { //获取属性的名称 String name = entry.getValue().getName(); //获取属性值 Field field = object.getClass().getDeclaredField(name); field.setAccessible(true); values[i]=field.get(object); i++; } } }orm.xml里面的class的映射类。ClassMapping.java
package edu.scut.framework; import java.util.HashMap; public class ClassMapping { //类名 private String name; //表名 private String table; //多个属性映射 private Map<String, PropertyMapping> propertyMappings = new HashMap<String, PropertyMapping>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTable() { return table; } public void setTable(String table) { this.table = table; } public Map<String, PropertyMapping> getPropertyMappings() { return propertyMappings; } public void setPropertyMappings(Map<String, PropertyMapping> propertyMappings) { this.propertyMappings = propertyMappings; } }orm.xml里面的Property的映射类。
PropertyMapping.javapackage edu.scut.framework; public class PropertyMapping { //属性名称 private String name; //字段名称 private String column; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColumn() { return column; } public void setColumn(String column) { this.column = column; } }f. 编写测试类
Test.java
package edu.scut.test; import edu.scut.dao.StudentDao; import edu.scut.entity.Student; //测试类 public class Test { public static void main(String[] args) { StudentDao dao = new StudentDao(); Student student = new Student(); student.setId(1); student.setName("郭靖"); student.setGender("男"); student.setAge(24); dao.saveStudent(student); } }g. 创建C3PO工具类,hu
C3P0Util.java
package edu.scut.util; import java.sql.ResultSet; import java.sql.SQLException; import javax.management.RuntimeErrorException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import com.mysql.jdbc.Connection; import com.mysql.jdbc.Statement; public class C3P0Util { //创建连接池对象 //使用无参的构造方法--使用默认的配置:default-config //规则:一个数据库,就使用一个连接池对象 private static DataSource ds = new ComboPooledDataSource(); /** * 提供获连接的方法 * @return 连接 */ public static Connection getConnection(){ try { Connection conn = (Connection) ds.getConnection(); return conn; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 返回数据源 * @return 数据源 */ public static DataSource getDataSource(){ return ds; } public static void close(ResultSet rs, Statement stmt,Connection conn){ try { if(rs!=null) rs.close(); if(stmt!=null) stmt.close(); if(conn!=null) conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } }c3p0的配置文件c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!-- 默认配置 --> <!-- 属性规则: name的值和方法名称相同--> <default-config> <property name="jdbcUrl">jdbc:mysql://localhost:3306/day31?useUnicode=true&characterEncoding=utf-8</property> <property name="user">root</property> <property name="password">root</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="initialPoolSize">5</property> <property name="maxPoolSize">12</property> <property name="checkoutTimeout">3000</property> </default-config> <!-- 命名配置,制定了名称--> <named-config name="empSysDB"> <property name="jdbcUrl">jdbc:mysql://localhost:3306/day31?useUnicode=true&characterEncoding=utf-8</property> <property name="user">root</property> <property name="password">root</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="initialPoolSize">5</property> <property name="maxPoolSize">15</property> <property name="checkoutTimeout">3000</property> </named-config> </c3p0-config>
注意:需要创建lib文件夹,导入必须的jar包。
四、Hibernate4框架的开发步骤
4.1 下载完整的包。
在hibernate官网下载完整zip包 。hibernate-release-4.3.8.Final.zip。
4.2 复制%hibernate%/lib/required目录下的所有jar包,再加上数据库驱动包.。
4.3 创建表(以后创建表可以交给Hibernate框架完成)
CREATE TABLE student( id INT PRIMARY KEY, NAME VARCHAR(20), gender VARCHAR(2), age INT );
package edu.scut.entity; public class Student { private int id; private String name; private String gender; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
注意:
a. 配置文件的名字理论上前缀不限,但后缀必须是小写字母xml,但提倡使用如下格式:实体类名.hbm.xml。如Student.hbm.xml。 b. 配置文件的 位置理论上不限制,但提倡放在与实体类同一个目录下。
Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- 实体类的映射 name:类名称 table:表名称 --> <class name="edu.scut.entity.Student" table="t_student"> <!-- 配置主键 --> <id name="id" column="t_id"> <!-- 主键策略 assigned:自己进行赋值 --> <generator class="assigned"></generator> </id> <!-- 映射属性 name:属性名称 column:字段名称--> <property name="name" column="t_name"></property> <property name="gender" column="t_gender"></property> <property name="age" column="t_age"></property> </class> </hibernate-mapping>
4.6 创建hibernate和MySQL的数据库连接配置文件
注意:
a. 理论上前缀不限,后缀必须是小写字母xml,但提倡使用如下格式:hibernate.cfg.xml
b. 配置文件的 位置理论上不限制,但提倡放在src目录下
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <!-- 启动hibernate的配置文件 --> <hibernate-configuration> <!-- sessionFactory类似于DataSource --> <session-factory> <!-- 1 连接数据库参数 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/day31?useUnicode=true&characterEncoding=utf-8</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!-- 2 hibernate的环境配置 --> <!-- hibernate的方言,指定hibernate生成的SQL语句的数据库版本--> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <!-- 3 ORM映射信息 --> <mapping resource="student.hbm.xml"/> </session-factory> </hibernate-configuration>
package edu.scut.test; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import edu.scut.entity.Student; //测试保存学生 public class TestDao { public static void main(String[] args) { //1 读取hibernate.cfg.xml文件,创建Configuration对象 //默认在src下找hibernate.cfg.xml文件,也可以配置在其他路径, //但是要用有参的configure("路径")将配置文件加载进来 Configuration configure = new Configuration().configure(); //2 创建SessionFactory对象 SessionFactory sessionFactory = null; //hibernate4以后的版本都是依赖服务注册器创建SessionFactory的 ServiceRegistry sr = new StandardServiceRegistryBuilder().applySettings(configure.getProperties()).build(); sessionFactory = configure.buildSessionFactory(sr); //3 创建session对象,封装了CRUD方法 Session session = sessionFactory.openSession(); Student student = new Student(); student.setId(3); student.setName("杨过"); student.setAge(18); student.setGender("男"); //4 开启事务(hibernate强制使用事务) session.beginTransaction(); //5 保存对象 session.save(student); //6 提交事务 session.getTransaction().commit(); //7 关闭资源 session.close(); sessionFactory.close(); } }
在此引用一个经典的图来说明问题。
六、Hibernate核心API详解
6.1 Configuration对象
6.1.1 Configuration对象负责加载和管理Hibernate的配置信息。
主要包括以下内容:
a. Hibernate运行的信息
- 数据库的url、用户名、密码、JDBC驱动类、数据库的连接池等
- 数据库的dialect、显示sql语句、格式化sql语句、自动维护表
b. 持久化类与数据库的映射关系
- *.hbm.xml
6.1.2 创建Configuration的两种方式
a. 通过属性文件的方式进行配置(hibernate.properties)的时候:
Configuration cfg = new Configuration();b. 通过xml文件的形式进行配置(hibernate.cfg.xml)的时候:
Configuration cfg = new Configuration().configure();
6.2 SessionRegistry对象(Hibernate4出现的,后面进行更进一步的学习)
ServiceRegistry 对象是 Service 的注册器, 它为Service提供了一个统一的加载 、初始化 、 存放 、获取机制。
6.3 SessionFactory对象
a. Configuration对象根据当前的配置信息生成 SessionFactory 对象。SessionFactory 对象一旦构造完毕,即被赋予特定的配置信息(SessionFactory 对象中保存了当前的数据库配置信息和所有映射关系以及预定义的SQL语句。同时,SessionFactory还负责维护Hibernate的二级缓存)。
Configuration cfg = new Configuration().configure(); SessionFactory sf = cfg.buildSessionFactory();b. SessionFactory是线程安全的。
c. SessionFactory是生成Session的工厂。Session session = sf.openSession();
d. 构造SessionFactory很消耗资源,一般情况下一个应用中只初始化一个 SessionFactory对象。
6.4 Session对象
a. Session是应用程序与数据库之间交互操作的一个单线程对象,是 Hibernate 运作的中心,所有持久化对象必须在session 的管理下才可以进行持久化操作。此对象的生命周期很短。Session 对象有一个一级缓存,显式执行flush 之前,所有的持久层操作的数据都缓存在session 对象处。相当于 JDBC 中的 Connection。
b. 持久化类与 Session 关联起来后就具有了持久化的能力。
c. Session是线程不安全的。
d. Session 类的方法:
取得持久化对象的方法: get() 、load()。
持久化对象都得保存,更新和删除:save()、update()、saveOrUpdate()、delete()。
开启事务: beginTransaction()。
管理 Session 的方法:isOpen()、flush()、 clear()、 evict()、 close()。
6.5 Transaction对象
a. 代表一次原子操作,它具有数据库事务的概念。所有持久层都应该在事务管理下进行,即使是只读操作。hibernate强制使用事务。
Transaction tx = session.beginTransaction();b. 常用方法:
commit():提交相关联的session实例。
rollback():回滚事务操作。
七、Hibernate配置详解
相关配置的解释在代码的注释当中。
7.1 hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!--1 连接数据库参数配置 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/day33a?useUnicode=true&characterEncoding=utf-8</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!--2 hibernate环境配置 --> <!-- 数据库方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property><!-- org.hibernate.dialect.MySQL5InnoDBDialect --> <!-- 是否显示SQL语句 --> <property name="hibernate.show_sql">true</property> <!-- 格式化SQL语句 --> <property name="hibernate.format_sql">true</property> <!-- 自动维护表 create:创建表,每次启动都会创建表 update:更新表,只是检查映射和当前的表结构是否一致,如果不一致就改变表的结构 --> <property name="hbm2ddl.auto">update</property> <!--3 映射信息:映射文件的路径 --> <mapping resource="edu/scut/a_hibernate/Student.hbm.xml"/> </session-factory> </hibernate-configuration>
7.2 Student.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="edu.scut.a_hibernate.Student" table="t_student"> <!--1 主键 --> <id name="id" column="t_id" type="integer"> <!-- 主键策略 --> <generator class="assigned"></generator> </id> <!--2 普通属性 --> <property name="name" column="t_name" type="string" length="20"></property> <property name="gender" column="t_gender" type="string" length="2"></property> <property name="age" column="t_age" type="integer"></property> <property name="grade" type="string" length="20"></property> <!-- 日期类型 --> <property name="birthday" column="t_birthday" type="date"></property> <!-- 字符文件:默认的是longtext --> <property name="charFile" column="t_charfile" type="string"></property> <!-- 字节文件:默认是tinyblob --> <property name="streamFile" column="t_streamfile" type="binary" length="200000"></property> </class> </hibernate-mapping>
7.3 主键策略
native | 根据底层数据库的能力,从identity、sequence、hilo中选择一个,灵活性更强。 |
<id name="id" column="id"> <generator class="native" /> </id> |
increment | 是由Hibernate在内存中生成主键,每次增量为1,不依赖于底层的数据库,因此所有的数据库都可以使用。 |
<id name="id" column="id"> <generator class="increment" /> </id> |
identity | 采用数据库生成的主键,用于为long、short、int类型生成唯一标识, Oracle 不支持自增字段。 |
<id name="id" column="id" type="long"> <generator class="identity" /> </id> |
sequence | DB2、Oracle均支持的序列,用于为long、short或int生成唯一标识。需要oracle创建sequence。 |
<id name="id" column="id" type="long"> <generator class="sequence"> <param name="sequence">seq_name</param> </generator> </id> |
uuid.hex | 使用一个128-bit的UUID算法生成字符串类型的标识符。 |
<id name="id" column="id"> <generator class="uuid.hex" /> </id> |
7.4 hibernate类型和java数据类型的对应
Hibernate映射类型 | Java类型 | 标准SQL类型 |
integer | java.lang.Integer | INTEGER |
long | java.lang.Long | BIGINT |
short | java.lang.Short | SMALLINT |
float | java.lang.Float | FLOAT |
double | java.lang.Double | DOUBLE |
big_decimal | java.math.BigDecimal | NUMRIC |
character | java.lang.String | CHAR(1) |
string | java.lang.String | VARCHAR |
byte | byte或者java.lang.Byte | TINYINT |
boolean | boolean或者java.lang.Boolean | BIT |