对象持久化
- 狭义的理解,“持久化”仅仅指把对象永久保存到数据库中
- 广义的理解,“持久化”包括和数据库相关的各种操作:
- 保存:把对象永久保存到数据库中
- 更新:更新数据库中对象(记录)的状态
- 删除:从数据库中删除一个对象
- 查询:根据特定的查询条件,把符合查询条件的一个或多个对象从数据库加载到内存中
ORM
- ORM(Object/Relation Mapping): 对象/关系映射
- 类—》表
- 对象—》表的行(记录)
- 属性—》表的列(字段)
- ORM的思想:将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作
- ORM 采用元数据来描述对象-关系映射细节, 元数据通常采用 XML 格式, 并且存放在专门的对象-关系映射文件中
开发步骤
配置文件
-
Hibernate 从其配置文件中读取和数据库连接的有关信息, 这个文件应该位于应用的 classpath 下
-
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="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.username">mysql</property>
<property name="hibernate.connection.password">mysql</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mysqldb</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.pool_size">1</property>
<!-- 配置数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 输出运行时生成的SQL语句-->
<property name="show_sql">true</property>
<!--指定是否对输出sql语句进行格式化-->
<property name="format_sql">true</property>
<!--指定程序运行时是否在数据库自动生成数据表-->
<property name="hbm2ddl.auto">update</property>
<!-- 列出所有的映射文件 -->
<mapping resource="org/hibernate/entity/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
hbm2ddl.auto:该属性可帮助程序员实现正向工程:create | update | create-drop | validate
create : 会根据 .hbm.xml 文件来生成数据表, 但是每次运行都会删除上一次的表 ,重新生成表, 哪怕二次没有任何改变
create-drop : 会根据 .hbm.xml 文件生成表,但是SessionFactory一关闭, 表就自动删除
update : 最常用的属性值,也会根据 .hbm.xml 文件生成表, 但若 .hbm.xml 文件和数据库中对应的数据表的表结构不同, Hiberante 将更新数据表结构,但不会删除已有的行和列
validate : 会和数据库中的表进行比较, 若 .hbm.xml 文件中的列在数据表中不存在,则抛出异常
持久化类
- 提供一个无参的构造器:使Hibernate可以使用Constructor.newInstance() 来实例化持久化类
- 提供一个标识属性(identifier property): 通常映射为数据库表的主键字段. 如果没有该属性,一些功能将不起作用,如:Session.saveOrUpdate()
- 为类的持久化类字段声明访问方法(get/set): Hibernate对JavaBeans 风格的属性实行持久化
- 使用非 final 类: 在运行时生成代理是 Hibernate 的一个重要的功能. 如果持久化类没有实现任何接口, Hibnernate 使用 CGLIB 生成代理. 如果使用的是 final 类, 则无法生成 CGLIB 代理
- 重写 eqauls 和 hashCode 方法: 如果需要把持久化类的实例放到 Set 中(当需要进行关联映射时), 则应该重写这两个方法
- Hibernate 不要求持久化类继承任何父类或实现接口,这可以保证代码不被污染(低侵入式设计)
映射文件
- Hibernate 采用 XML 格式的文件来指定对象和关系数据之间的映射
- 在运行时 Hibernate将根据这个映射文件来生成各种 SQL 语句
- 映射文件的扩展名为 XXX.hbm.xml
package org.hibernate.entity;
public class User{
// Fields
private int id;
private String name;
private String password;
private String type;
// Constructors
/** default constructor */
public User() {
}
/** full constructor */
public User(int id, String name, String password, String type) {
this.id = id;
this.name = name;
this.password = password;
this.type = type;
}
// Property accessors
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
}
<?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>
<!--name指定持久化类的类名,table指定数据表的表名-->
<class name="org.hibernate.entity.User" table="USER">
<!--将user类中的id属性映射为数据表USER中的主键USER_ID-->
<id name="id" type="java.lang.Integer" column="USER_ID">
<!--主键生成策略,意味着id的自增长方式采用数据库的自增长模式-->
<generator class="increment" />
</id>
<!--映射User类中的name属性-->
<property name="name" type="java.lang.String">
<column name="NAME" length="20"></column>
</property>
<!--映射User类中的password属性-->
<property name="password" type="java.lang.String" >
<column name="PASSWORD" length="12"></column>
</property>
<!--映射User类中的type属性-->
<property name="type" type="java.lang.String" >
<column name="TYPE" length="6"></column>
</property>
</class>
</hibernate-mapping>
访问数据库
- 工具类
package org.hibernate.entity;
import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static Configuration configuration = new Configuration();
//创建线程局部变量threadLocal,用来保存Hibernate的Session
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
//使用静态代码块初始化Hibernate
static {
try {
Configuration cfg=new Configuration().configure(); //读取配置文件hibernate.cfg.xml
//创建ServiceRegistry对象
ServiceRegistry serviceRegistey=new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();
//创建SessionFactory
sessionFactory=cfg.buildSessionFactory(serviceRegistey);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
//获得SessionFactory实例
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
//获得ThreadLocal 对象管理的Session实例.
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
//通过SessionFactory对象创建Session对象
session = (sessionFactory != null) ? sessionFactory.openSession(): null;
//将新打开的Session实例保存到线程局部变量threadLocal中
threadLocal.set(session);
}
return session;
}
//关闭Session实例
public static void closeSession() throws HibernateException {
//从线程局部变量threadLocal中获取之前存入的Session实例
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.close();
}
}
//重建SessionFactory
public static void rebuildSessionFactory() {
try {
configuration.configure("/hibernate.cfg.xml"); //读取配置文件hibernate.cfg.xml
sessionFactory = configuration.buildSessionFactory(); //创建SessionFactory
} catch (Exception e) {
System.err.println("Error Creating SessionFactory ");
e.printStackTrace();
}
}
//关闭缓存和连接池
public static void shutdown() {
getSessionFactory().close();
}
}
- DAO
package org.hibernate.dao;
import java.util.List;
import org.hibernate.entity.User;
//创建UserDAO接口
public interface UserDAO {
void save(User user); //添加用户
User findById(int id); //根据用户标识查找指定用户
void delete(User user); //删除用户
void update(User user); //修改用户信息
}
package org.hibernate.dao;
import org.hibernate.*;
import org.hibernate.entity.*;
public class UserDAOImpl implements UserDAO {
//添加用户
public void save(User user){
Session session= HibernateUtil.getSession(); //生成Session实例
Transaction tx = session.beginTransaction(); //创建Transaction实例
try{
session.save(user); //使用Session的save方法将持久化对象保存到数据库
tx.commit(); //提交事务
} catch(Exception e){
e.printStackTrace();
tx.rollback(); //回滚事务
}finally{
HibernateUtil. closeSession(); //关闭Session实例
}
}
//根据用户标识查找指定用户
public User findById(int id){
User user=null;
Session session= HibernateUtil.getSession(); //生成Session实例
Transaction tx = session.beginTransaction(); //创建Transaction实例
try{
user=(User)session.get(User.class,id);//使用Session的get方法获取指定id的用户到内存中
tx.commit(); //提交事务
} catch(Exception e){
e.printStackTrace();
tx.rollback(); //回滚事务
}finally{
HibernateUtil. closeSession(); //关闭Session实例
}
return user;
}
//删除用户
public void delete(User user){
Session session= HibernateUtil.getSession(); //生成Session实例
Transaction tx = session.beginTransaction(); //创建Transaction实例
try{
session.delete(user); //使用Session的delete方法将持久化对象删除
tx.commit(); //提交事务
} catch(Exception e){
e.printStackTrace();
tx.rollback(); //回滚事务
}finally{
HibernateUtil. closeSession(); //关闭Session实例
}
}
//修改用户信息
public void update(User user){
Session session= HibernateUtil.getSession(); //生成Session实例
Transaction tx = session.beginTransaction(); //创建Transaction实例
try{
session.update(user); //使用Session的update方法更新持久化对象
tx.commit(); //提交事务
} catch(Exception e){
e.printStackTrace();
tx.rollback(); //回滚事务
}finally{
HibernateUtil. closeSession(); //关闭Session实例
}
}
}
- test
package org.hibernate.test;
import org.hibernate.dao.*;
import org.hibernate.entity.User;
import org.junit.Before;
import org.junit.Test;
public class UserTest {
@Before
public void setUp() throws Exception {
}
@Test //test注释表明该方法为一个测试方法
public void testSave() {
UserDAO userdao=new UserDAOImpl();
try{
User u=new User(); //创建User对象
//设置User对象中的各个属性值
u.setId(20);
u.setName("zhangsan");
u.setPassword("456");
u.setType("admin");
userdao.save(u); //使用userdaoimpl的save方法将User对象存入数据库
}catch(Exception e){
e.printStackTrace();
}
}
}
总结
- 编写持久化类: POJO + 映射文件
- 获取 Configuration对象
- 获取 SessionFactory 对象
- 获取 Session,打开事务
- 用面向对象的方式操作数据库
- 关闭事务,关闭 Session
Configuration
- Configuration 类负责管理 Hibernate 的配置信息。包括如下内容
-
Hibernate 运行的底层信息:数据库的URL、用户名、密码、JDBC驱动类,数据库Dialect,数据库连接池等(对应 hibernate.cfg.xml 文件)
- 创建 Configuration的两种方式:
- 属性文件(hibernate.properties):Configuration cfg = new Configuration();
- Xml文件(hibernate.cfg.xml):Configuration cfg = new Configuration().configure();
File file = new File(“simpleit.xml”);
Configuration cfg = new Configuration().configure(file);
SessionFactory
- 针对单个数据库映射关系经过编译后的内存镜像,是线程安全的
- SessionFactory 对象一旦构造完毕,即被赋予特定的配置信息
- SessionFactory是生成Session的工厂
- 构造 SessionFactory 很消耗资源,一般情况下一个应用中只初始化一个 SessionFactory 对象
- Hibernate4 新增了一个 ServiceRegistry 接口,所有基于 Hibernate 的配置或者服务都必须统一向这个 ServiceRegistry 注册后才能生效
Configuration cfg=new Configuration().configure(); //读取配置文件hibernate.cfg.xml
//创建ServiceRegistry对象
ServiceRegistry serviceRegistey=new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();
//创建SessionFactory
sessionFactory=cfg.buildSessionFactory(serviceRegistey);
Session接口
- Session 是应用程序与数据库之间交互操作的一个单线程对象
- 是 Hibernate运作的中心,所有持久化对象必须在 session 的管理下才可以进行持久化操作
- 此对象的生命周期很短
- Session 对象有一个一级缓存,显式执行flush 之前,所有的持久层操作的数据都缓存在 session 对象处
-
相当于 JDBC 中的 Connection
- 持久化类与 Session 关联起来后就具有了持久化的能力
方法
- get() ,load():取得持久化对象的方法
- save(),update(),saveOrUpdate(),delete():持久化对象都得保存,更新和删除
- beginTransaction():开启事务
- isOpen(),flush(),clear(), evict(), close():管理 Session 的方法
save
- Session 的 save() 方法使一个临时对象转变为持久化对象
- 把 News 对象加入到 Session 缓存中, 使它进入持久化状态
- 选用映射文件指定的标识符生成器, 为持久化对象分配唯一的 OID
- 计划执行一条 insert 语句:在 flush 缓存的时候
- Hibernate 通过持久化对象的 OID 来维持它和数据库相关记录的对应关系
- 当 News 对象处于持久化状态时, 不允许程序随意修改它的ID
- 当对一个 OID 不为 Null 的对象执行 save() 方法时, 会把该对象以一个新的 oid 保存到数据库中; 但执行 persist() 方法时会抛出一个异常
get&load
- 都可以根据跟定的 OID 从数据库中加载一个持久化对象
- 当数据库中不存在与 OID 对应的记录时, load() 方法抛出 ObjectNotFoundException 异常, 而 get() 方法返回 null
- 两者采用不同的延迟检索策略:load 方法支持延迟加载策略。而get 不支持
update
- Session 的 update() 方法使一个游离对象转变为持久化对象, 并且计划执行一条 update 语句
- 若希望 Session 仅当修改了 News 对象的属性时, 才执行 update() 语句, 可以把映射文件中
<class>
元素的 select-before-update 设为 true. 该属性的默认值为 false - 当 update() 方法关联一个游离对象时, 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常
- 当 update() 方法关联一个游离对象时, 如果在数据库中不存在相应的记录, 也会抛出异常
saveOrUpdate
- Session 的 saveOrUpdate() 方法同时包含了 save() 与 update() 方法的功能
delete
- Session 的 delete() 方法既可以删除一个游离对象, 也可以删除一个持久化对象
- 计划执行一条 delete 语句
- 把对象从 Session 缓存中删除, 该对象进入删除状态
- Hibernate 的 cfg.xml 配置文件中有一个 hibernate.use_identifier_rollback 属性, 其默认值为 false, 若把它设为 true, 将改变 delete() 方法的运行行为: delete() 方法会把持久化对象或游离对象的 OID 设置为 null, 使它们变为临时对象
Query接口
- query接口是hibernate的查询接口,用于向数据库中查询对象,并控制执行查询的过程。
- query中包装了一个HQL查询语句
使用步骤
- 通过session实例的createQuery方法创建一个query实例
- 设置动态参数,使用setter方法
- 执行查询语句
常用方法
- setter方法:设置查询语句中的参数,针对不同的数据类型需要用到不同的setter方法
- list方法:用于执行查询语句,并将查询结果以list的形式返回
- iterator方法:用于查询语句,返回结果是一个iterator对象,在读取时只能按照顺序方式读取
- uniqueResult方法:用于返回唯一的结果,在确保最多只有一条记录的情况下可以使用该方法。返回的结果可以直接转换为相应的对象
- executeUpdate方法:支持HQL语句的更新和删除操作,建议更新时采用此方法
- setFirstResult方法:可以设置所获得的第一条记录的位置,从0开始计算,用于筛选选取记录的范围。
- setMaxResults方法:设置结果集的最大记录数,可以和setFirstResult结合使用,限制结果集的范围,实现分页功能
Query query=session.createQuery("from User");
int currentPage=1;
int pageSize=10;
query.setFirstResult( (currentPage-1)*pageSize);
query.setMaxResults(pageSize);
List Users=query.list();
session.close();
Criteria接口
- 也是查询接口,它允许创建并执行面向对象方式的查询
- 更擅长执行动态查询
常用类
- Criteria:表示一次查询
- Criterion:表示一个查询条件,可以通过Restrictions工具类来创建
- Restriction:表示查询条件的工具类。提供了大量的静态方法,如eq(等于)、ge(大于等于)、between
使用步骤
- 利用session实例的createCriteria方法创建一个实例
- 设定查询条件,通过Expression类或Restrictions类创建查询条件的实例,即Criterion实例
- 调用Criteria的list()方法来执行查询语句
List users=session.createCriteria(User.class).add(Restrictions.eq("name","jack")).list();
/*
在运行时,转化为:
select * from user where name="jack";
*/
常用方法
- add方法:设置查询条件,可以追加任意个add方法
- addOrder方法:设置查询结果集的排序规则,相当于sql的order by语句,参数为Order类的实例
List users=session.createCriteria(User.class)
.add(Restrictions.eq("name","jack"))
.addOrder(Order.asc("name"))
.list();
- createCriteria方法:提供了对多表查询的方法
List users=session.createCriteria(User.class)
.add(Restrictions.eq("name","jack"))
.createCriteria("role")
.add(Restrictions.eq("rolename","admin"))
.list();
/*
select * from user,role where name="jack" and rolename="admin"
*/
- list方法:执行数据查询,并将查询结果返回
- scroll方法:与list方法类似,将结果集以ScrollableResults类型返回
- setFetchModel方法:设置抓取策略
- setFetchSize方法:指定一次从数据库中提取数据的数量大小,通过调用Statement.setFetchSize()方法实现
- setMaxResults方法:用于设置从数据库中取得记录的最大行数,实现分页功能时将每页显示数据的数目作为参数传入该方法即可
- setFirstResult方法:设置所获取带的第一个记录的位置,从0开始,用于分页查询
- setProjection方法:主完成一些聚合查询和分组查询。
List users=session.createCriteria(User.class)
.setProjection(Projections.projectionList()
.add(Projections.rowCount())
.add(Projections.avg("age"))
.add(Projections.max("age"))
.ad(Projections.min("age"))
.add(Projections.groupProperty("sex"))
).list();
/*
select sex,count(*),avg(age),max(age),min(age) from user group by sex
*/
- uniqueResult方法:可以得到唯一的查询结果,该结果为一个对象
Transaction
- 代表一次原子操作,它具有数据库事务的概念
- 所有持久层都应该在事务管理下进行,即使是只读操作
Transaction tx = session.beginTransaction();
方法
- commit():提交相关联的session实例
- rollback():撤销事务操作
- wasCommitted():检查事务是否提交