一 简介
1.什么是ORM
Object/Relation Mapping,即对象/关系映射。可以将其理解为一种规范,具体的ORM框架可以作为应用程序和数据库的桥梁。面向对象程序设计语言与关系数据库发展不同步时,需要一种中间解决方案,而ORM就是这样的解决方案 。ORM工具的唯一作用就是:把对持久化对象的保存、删除、修改等操作,转换成对数据库的操作。
ORM并不是一种具体的产品,而是一类框架的总称,它概述了这类框架的基本特征:完成面向对象的程序设计语言到关系数据库的映射。
基于ORM框架完成映射后,既可以利用面向对象程序设计语言的简单易用性,又可以利用关系数据库的技术优势。
目前ORM的产品非常多,如Apache组织的QIB,Oracle的TopLink,JDO,JPA等等。
2.Hibernate
Hibernate是目前最流行的ORM框架之一,它是一个面向Java环境的对象/关系数据库映射工具
它也是一个轻量级的O/R Mapping框架,是目前最流行的持久层解决方案,较之另一个持久层框架MyBATIS,更具有面向对象的特征
Hibernate是ORM规范的实现框架,所有的ORM框架的作用:负责把面向对象的持久化操作,转化为数据库标准SQL语句去执行
但是Hibernate不一定能提高程序的性能,可扩展性和可维护性
ORM规范映射思想:一个表映射成一个类;一行记录(一条数据)映射成一个对象,一列(一个字段)映射成对象的属性
3.下载:www.hibernate.org/downloads
解压后的文档结构如下:
documentation:各种相关的文档,包括Hibernate的参考文档和API文档等等
lib:存放了核心类库以及编译和运行所依赖的第三方类库。其中lib路径下的required子目录下保存了运行Hibernate的核心类库,以及必需的第三方类库
project:存放了Hibernate各种相关项目的源代码
lgpl.txt、logo等杂项文件
二 具体实现
1.PO(持久化对象)
Person.class
public class Person {
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", password=" + password
+ ", birthday=" + birthday + "]";
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPassword() {
return password;
}
public void setPassword(int password) {
this.password = password;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
private Integer id;
private String name;
private int password;
private Date birthday;
public Person() {
super();
}
public Person(String name, int password, Date birthday) {
super();
this.name = name;
this.password = password;
this.birthday = birthday;
}
}
如上所示,Hibernate采用了POJO(普通的、传统的Java对象)作为持久化类,这就是Hibernate被称为低侵入式设计的原因。Hibernate不要求持久化类继承任何父类,或者实现任何接口,这样可以保证代码不被污染。
注意:持久化设计时持久化类通常建议使用一个持久化标识符(ID),并且建议使用封装类(基本类型有默认值)。通常要给定一个无参构造器,因为有些操作是反射进行的,属性通常要提供getter/setter方法,持久化类不能是final类型。持久化类中如果使用了集合类型数据,只能使用接口类型进行声明(List,Set,Map),如:List list=new ArrayList();
如何将一个POJO转换为PO呢?也就是说如何将上面的Person类进行持久化呢?Hibernate提供了三种方式:
(1)使用持久化注解(以JPA标准注解为主)
(2)使用JPA提供的xml配置文件,很少使用
(3)使用Hibernate传统的xml映射文件(*.hbm.xml文件的形式)
这里使用第一种和第三种方法作为示例
a.建立Person.hbm.xml
该文件是一个持久化映射文件,将Java对象映射到数据库表。几乎所有的ORM工具都需要一个数据库字段与JavaBean属性匹配的映射文件,在hibernate中一般命名为*.hbm.xml
<?xml version="1.0"? >
<!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="HibernateTest.Person" table="t_person"> <!-- 类对应到数据库中的表 -->
<id name="id"> <!-- Person类中的id为class的id,id必须指定,name指定持久化类的持久化标识符名称,generate指定id生成策略-->
<generator class="native"></generator> <!-- 该id为本地产生 -->
</id>
<property name="name" column="t_name"></property>
<property name="password" length="6"></property>
<property name="birthday"></property>
</class>
</hibernate-mapping>
注意:在xml文件中,name对应的是java类中的对象,property为其他属性映射配置,name为持久化类中需要映射的属性名,column指定表的列名(不写表示默认使用属性名),length指定列存储数据长度,type指定字段类型
b.使用持久化注解
将Person.java修改为:
@Entity
@Table(name="t_person")
public class Person {
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", password=" + password
+ ", birthday=" + birthday + "]";
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPassword() {
return password;
}
public void setPassword(int password) {
this.password = password;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="t_name")
private String name;
@Column(length=6)
private int password;
private Date birthday;
public Person() {
super();
}
public Person(String name, int password, Date birthday) {
super();
this.name = name;
this.password = password;
this.birthday = birthday;
}
}
其中,@Entity注解声明该类是一个持久化类,@Table指定该类映射的表,@Id指定该类的标识属性,@GeneratedValue指定主键生成策略,@Column用于指定数据库中对应列的详细信息。由此可见,PO=POJO+持久化注解
2.Hibernate配置文件
在生成PO文件后,就要在hibernate.cfg.xml中配置各种信息。
hibernate的project->etc中有各种模板,将其中的hibernate.cfg.xml复制到项目的src路径中。在这里展示一下mySQL数据库的连接方式:
<hibernate-configuration>
<session-factory>
//配置成数据库方言模式,更方便数据库识别
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///mydb</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
//数据库连接池
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.min_size">1</property>
<property name="hibernate.c3p0.timeout">5000</property>
//根据需要自动创建数据库表
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<property name="hibernate.format_sql">true</property>
//这个是Person.hbm.xml对应的配置
<mapping resource="HibernateTest\Person.hbm.xml"></mapping>
//这个对应的是持久化注解的PO类的路径
<mapping class="HibernateTest.Person"></mapping>
</session-factory>
</hibernate-configuration>
注意:各种数据库的连接方式可以具体在hibernate.properties.template文件查找,该文件都有详细的定义。其中,mapping根据PO持久化的方式有两种选择。
hibernate通过C3P0数据源来管理数据库连接,当程序创建数据源实例时,系统会一次性创建多个数据库连接,并将这些数据库连接保存在连接池中。程序访问数据库时,直接从连接池中取出一个空闲的数据库连接;访问结束后,无需关闭数据库直接将连接还给连接池即可。通过这种方式,可以避免频繁的获取数据库连接、关闭数据库连接所导致的性能下降。将optional路径下的C3P0的JAR包导入项目路径下。
其中,<property name="hibernate.hbm2ddl.auto">update</property>,update表示程序启动时没有就创建表,有就检查有没有更新(推荐使用)
3.编写测试程序test.java
public class test {
public static void main(String[] args) {
//创建Configuration实例,并加载配置文件(hibernate.cfg.xml),唯一的作用是创建SessionFactory,一旦创建完成就被丢弃
Configuration config=new Configuration().configure();
SessionFactory factory=config.buildSessionFactory();
Session session=factory.openSession();
Transaction tx=session.beginTransaction();
Person p=new Person(123,"admin",123456,new java.util.Date());
//将Person对象持久化到数据库
session.save(p);
//事务提交
tx.commit();
session.close();
//关闭SessionFactory,通常不用手动关闭
factory.close();
}
}
结果:
Hibernate:
create table t_person (
id integer not null auto_increment,
t_name varchar(255),
password integer,
birthday datetime,
primary key (id)
) ENGINE=InnoDB
Hibernate:
insert
into
t_person
(t_name, password, birthday)
values
(?, ?, ?)
同时数据库中建立了一个表为t_person,字段分别为id(1),t_name(admin),password(123456),birthday(...)
三 Hibernate的体系结构
1.SessionFactory
这是Hibernate的关键对象,它是单个数据库映射关系经过编译后的内存镜像,是线程安全的。应用程序从SessionFactory中获得Session实例,它在多个线程间进行共享。通常情况下,整个应用只有唯一的一个会话工厂。但是如果访问多个数据库,就需要对每一个数据库使用一个会话工厂。会话工厂缓存了生成的SQL语句和hibernate在运行时使用的映射元数据。
2.Session
它是应用程序与持久存储层之间交互操作的一个单线程对象,所有的PO必须在Session管理下才可以进行持久化操作。
生存期很短,底层封装了JDBC连接,也是Transaction的工厂。
Session有一个必选的一级缓存,显式执行flush之前,所有持久化操作都在缓存中。
3.Transaction
代表一次原子操作,具有数据库事务的概念。将应用代码从底层的事务实现中抽象出来,这可能是一个JDBC事务,一个JTA用户事务或是一个公共对象请求代理结构(CORBA),允许应用通过一组一致的API控制事务边界。这有助于保持Hibernate应用在不同类型的执行环境或容器中的可移植性。使用Hibernate进行操作(增删改)必须显示的调用它,即必须要commit。
整个的逻辑结构为:
业务逻辑层:业务对象(对象、属性、关联、继承)-->Person.java
持久化层: ORM API/ORM 实现-->Person.hbm.xml
JDBC-->hibernate.cfg.xml
数据库层:关系型数据库
四 改变持久化对象状态的方法
1.持久化实体
包括save()和persist()两种方法,如:
Person p=new Person();
p.setName("lyy");
session.save(p);
save()与persist()方法的区别:使用save()方法时会返回该持久化对象的标识属性值,即主键值;使用persist()方法时,不会返回任何值。程序执行save()方法会立即将持久化对应的数据插入到数据库,而persist()方法则保证当它在一个事务外部被调用时,并不立即转换成insert语句,在需要封装一个长会话时,这个方法尤为重要。
2.根据主键加载持久化类
主要包括load()和get()方法,如:
Person p=session.load(Person.class,1);
load()和get()的区别:主要区别在于是否延迟加载。load()方法具有延迟加载功能,Load()不会立即访问数据库,当试图加载的记录不存在时,可能返回一个未初始化的代理对象;而get()方法总是立即访问数据库,当记录不存在时,直接返回null
3.更新持久化实体
Person p=session.load(Person.class,1);
p.setPassword("345345");
也就是说,使用load()方法后就可以直接对实体进行更新。
4.使用saveOrUpdate()
saveOrUpdate()表示新增或更新,若id存在就是更新,不存在就报更新失败;如果不给id,就是新增
public class test {
private static void testSaveOrUpdate(){
Configuration config=new Configuration().configure();
SessionFactory factory=config.buildSessionFactory();
Session session=factory.openSession();
Transaction tx=session.beginTransaction();
Person p=new Person();
p.setId(3);
p.setPassword(12344);
session.saveOrUpdate(p);
tx.commit();
session.close();
}
public static void main(String[] args) {
testSaveOrUpdate();
}
}
修改成功后,密码为12344,其他为空
5.使用merge()
public class test {
private static void merge(){
Configuration config=new Configuration().configure();
SessionFactory factory=config.buildSessionFactory();
Session session=factory.openSession();
Transaction tx=session.beginTransaction();
Person p=(Person)session.get(Person.class, 3);
p.setName("lyy");
Person p2=(Person)session.merge(p);
p2.setBirthday(new java.util.Date());
tx.commit();
session.close();
}
public static void main(String[] args) {
merge();
}
}
注:持久化状态对象会在session关闭的时候如果内存数据和表数据不一致,将自动同步到数据库表
或者:
private static void testSaveOrUpdate(){
Configuration config=new Configuration().configure();
SessionFactory factory=config.buildSessionFactory();
Session session=factory.openSession();
Transaction tx=session.beginTransaction();
Person p=new Person();
p.setId(3);
p.setName("kui");
session.merge(p);
tx.commit();
session.close();
}
结果:name为kui,其他为空,相当于update
6.删除有两种方法:
删除持久化状态对象:
public class test {
public static void main(String[] args) {
Configuration config=new Configuration().configure();
SessionFactory factory=config.buildSessionFactory();
Session session=factory.openSession();
Transaction tx=session.beginTransaction();
Person p=(Person)session.load(Person.class,3);
session.delete(p);
tx.commit();
session.close();
}
}
删除临时化状态对象:
public static void main(String[] args) {
Configuration config=new Configuration().configure();
SessionFactory factory=config.buildSessionFactory();
Session session=factory.openSession();
Transaction tx=session.beginTransaction();
Person p=new Person();
p.setId(4);
session.delete(p);
tx.commit();
session.close();
}
使用junit进行测试:
在项目中new->Junit Test Case->选中setUp()和tearDown()方法,即初始化和最后的资源释放
public class myTest {
SessionFactory factory=null;
Session session=null;
Transaction tx=null;
@Before
public void setUp() throws Exception {
System.out.println("---初始化数据---");
Configuration config=new Configuration().configure();
factory=config.buildSessionFactory();
session=factory.openSession();
tx=session.beginTransaction();
}
@After
public void tearDown() throws Exception {
System.out.println("---释放资源---");
if(session.isOpen()){
session.close();
}
}
@Test
public void test() {
Person p=new Person(2,"admin",123,new Date());
session.persist(p);
tx.commit();
}
}
run as junit test->查看结果