O/R Mapping
对象关系映射(Object Relational Mapping,简称ORM)技术,是通过使用描述对象和数据库之间映射的元数据,将Java程序中的对象自动持久化到关系数据库中。
对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。
Hibernate是一个开放源码的、非常优秀、成熟的O/R Mapping框架。它提供了强大、高性能的Java对象和关系数据的持久化和查询功能。
Hibernate 只是一个将持久化类与数据库表相映射的工具,每个持久化类实例均对应于数据库表中的一条数据行。可以使用面向对象的方法操作此持久化类实例,完成对数据库表的插入、删除、修改等操作。
利用Hibernate操作数据库,我们通过应用程序经过Hibernate持久层来访问数据库,其实Hibernate完成了以前JDBC的功能,不过Hibernate使用面向对象的方法操作数据库。
Hibernate体系结构
Hibernate核心接口
(1) Configuration接口
Configuration 接口负责管理Hibernate 的配置信息。为了能够连上数据库必须配置一些属性,这些属性包括:
数据库URL
数据库用户
数据库用户密码
数据库JDBC驱动类
数据库dialect,用于对特定数据库提供支持,其中包含了针对特定数据库特性的实现。
/*创建一个配置对象,读取配置文件*/
Configuration config = new Configuration();
config.configure("/hibernate.cfg.xml");
(2) SessionFactory接口
应用程序从SessionFactory(会话工厂)里获得Session(会话)实例。这里用到了一个设计模式即工厂模式,用户程序从工厂类SessionFactory中取得Session的实例。SessionFactory不是轻量级的。它占的资源比较多,所以它应该能在整个应用中共享。一个项目通常只需要一个SessionFactory就够了,但是当项目要操作多个数据库时,必须为每个数据库指定一个SessionFactory。
会话工厂缓存了生成的SQL语句和Hibernate在运行时使用的映射元数据。它也保存了在一个工作单元中读入的数据并且可能在以后的工作单元中被重用(只有类和集合映射指定了使用这种二级缓存时才会如此)Session类。
/*通过配置对象产生一个会话工厂*/
SessionFactory factory=config.buildSessionFactory();
(3) Session接口
该接口是Hibernate使用最多的接口。Session不是线程安全的,它代表与数据库之间的一次操作。Session是持久层操作的基础,相当于JDBC中的Connection。然而在Hibernate中,实例化的Session是一个轻量级的类,创建和销毁它都不会占用很多资源。Session通过SessionFactory打开,在所有的工作完成后,需要关闭。但如果在程序中,不断地创建以及销毁Session对象,则会给系统带来不良影响。所以有时需要考虑session的管理合理的创建合理的销毁。
/*通过工厂产生一个会话*/
Session session=factory.openSession();
(4) Query类
Query类可以很方便地对数据库及持久对象进行查询,它可以有两种表达方式:查询语句使用HQL(HQL是Hibernate Query Lanaguage的简称,是Hibernate配备的一种非常强大的查询语言,类似于SQL)或者本地数据库的SQL语句编写。
/*通过会话产生一个查询对象*/
Query query = session.createQuery("from Dept");
/*通过查询对象查询数据库,返回集合*/
List list = query.list();
for (int i = 0; i < list.size(); i++) {
Dept dept = (Dept) list.get(i);
System.out.println(dept.getDname());
}
(5) Transaction接口
如果你向数据库中增加数据或修改数据时,需要使用事务处理,这时你需要Transaction接口。Transaction接口是对实际事务实现的一个抽象,该接口可以实现JDBC的事务、JTA中的UserTransaction、甚至可以是CORBA事务等跨容器的事务。之所以这样设计是能让开发者能够使用一个统一事务的操作界面,使得自己的项目可以在不同的环境和容器之间方便地移值。
简单例子入门
首先准备好所需jar包
Hibernate官网下载最新jar包下载地址,本例采用5.0.12版的jar包。注意除了Hibernate的jar包外还需 mysql的连接jar包
构建环境
采用Eclipse中自带的Ant工具进行构建项目环境。创建数据库和表,为Hibernate测试提供环境。
代码演示
1.hibernate.cfg.xml,该文件默认应该放在src目录下。
<?xml version="1.0" encoding="UTF-8"?>
<!-- 指定Hibernate配置文件的DTD信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- hibernate- configuration是连接配置文件的根元素 -->
<hibernate-configuration>
<session-factory>
<!-- 指定连接数据库所用的驱动 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 指定连接数据库的url,hibernate连接的数据库名 -->
<property name="connection.url">jdbc:mysql://localhost:3306/hib</property>
<!-- 指定连接数据库的用户名 -->
<property name="connection.username">root</property>
<!-- 指定连接数据库的密码 -->
<property name="connection.password">1234</property>
<!-- 指定连接池里最大连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 指定连接池里最小连接数 -->
<property name="hibernate.c3p0.min_size">1</property>
<!-- 指定连接池里连接的超时时长 -->
<property name="hibernate.c3p0.timeout">5000</property>
<!-- 指定连接池里最大缓存多少个Statement对象 -->
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.c3p0.validate">true</property>
<!-- 指定数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<!-- 根据需要自动创建数据表 -->
<property name="hbm2ddl.auto">update</property>
<!-- 显示Hibernate持久化操作所生成的SQL -->
<property name="show_sql">true</property>
<!-- 将SQL脚本进行格式化后再输出 -->
<property name="hibernate.format_sql">true</property>
<!-- 罗列所有的映射文件 -->
<mapping resource="cn/hncu/domain/Student.hbm.xml"/>
<!-- 下面是映射 以注解的形式 关联的类-->
<mapping class="cn.hncu.domain.Person"/>
</session-factory>
</hibernate-configuration>
2.hibernate框架加载工具,具体为何这样初始化sessionFactory我也不知道,反正文档提供的是该方式! O_O 把工具做成一个单线程共享一个session的形式,方便事务操作。
package cn.hncu.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
/**
*   Hibernate框架加载工具
* <br/><br/><b>CreateTime:</b><br/>    2018年10月2日 下午4:39:59
* @author 宋进宇 <a href='mailto:447441478@qq.com'>447441478@qq.com</a>
*/
public class HibernateUtil {
private static SessionFactory sessionFactory;
//为实现单个线程共享单个session
private static ThreadLocal<Session> tlPool;
static {
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
try {
sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
tlPool = new ThreadLocal<Session>();
}
catch (Exception e) {
// The registry would be destroyed by the SessionFactory, but we had trouble building the SessionFactory
// so destroy it manually.
StandardServiceRegistryBuilder.destroy( registry );
throw new RuntimeException(e);
}
}
/**
* 获取 SessionFactory 对象
* @return SessionFactory 对象
*/
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* 获取一个 Session 对象
* @return Session 对象
*/
public static Session getSession() {
Session session = tlPool.get();
if( session == null || !session.isOpen()) {
session = sessionFactory.openSession();
tlPool.set(session);
}
return session;
}
}
3.准备一个POJO类,该类与数据库中的student表相对应。故意取类属性和表字段不同,为演示Mapping中映射配置。
package cn.hncu.domain;
/*
create table student(
id varchar(32) primary key,
name varchar(20),
age int,
deptId varchar(32)
);
*/
public class Student {
private String studId;
private String studName;
private Integer age;
private String deptId;
public Student() {
}
@Override
public String toString() {
return "Student [studId=" + studId + ", studName=" + studName + ", age=" + age + ", deptId=" + deptId + "]";
}
public String getStudId() {
return studId;
}
public void setStudId(String studId) {
this.studId = studId;
}
public String getStudName() {
return studName;
}
public void setStudName(String studName) {
this.studName = studName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDeptId() {
return deptId;
}
public void setDeptId(String deptId) {
this.deptId = deptId;
}
}
4.Mapping配置
<?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 package="cn.hncu.domain">
<!-- 写配置文件时站的立场是类(Object) -->
<class name="Student" catalog="hib" table="student">
<!-- 主键建议使用id标签,效率更高 -->
<id name="studId" type="java.lang.String">
<column name="id" length="32"></column>
<generator class="assigned"></generator> <!-- id代码控制插入 -->
</id>
<!--
<property name="studName" type="java.lang.String">
<column name="name" length="20"></column>
</property>
-->
<!-- 下面这种方式等价于上面 -->
<property name="studName" type="java.lang.String" column="name" length="20"/>
<property name="age" type="java.lang.Integer">
<column name="age"></column>
</property>
<property name="deptId" type="java.lang.String">
<column name="deptId" length="32"></column>
</property>
</class>
</hibernate-mapping>
测试
插入一条记录,采用 session.save(obj) 就可以插入了,比以往的jdbc方便多了,不用体系出sql语句完全面向对象了。
public static void save() {
Student student = new Student();
student.setStudId("s003");
student.setStudName("马克");
student.setAge(22);
student.setDeptId("d002");
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
session.save(student);
tx.commit();
session.close();
}
结果:
查询所有student记录,采用HQL(hibernate query langue)进行查询,半面向对象吧。
public static void query() {
Session session = HibernateUtil.getSession();
Query query = session.createQuery("from Student");
List list = query.list();
System.out.println(list);
}
结果: