hibernate连接之前,现在Myeclipse Database Exeplorer中建立数据库的连接
在mysql的demo中建立表
CREATE TABLE Student (
id int NOT NULL auto_increment,
username varchar(200) NOT NULL,
password varchar(20) NOT NULL,
age int,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=GBK
术语解释:DAO 是Data Access Object,数据访问对象的缩写。POJO 是Plain and
Old Java Object,普通和旧式的Java 对象的缩写,也就是普通Java 类的意思。进行Java
开发时经常会看到各式各样的缩写词。
7.2 Hibernate 一览
7.2.1 简介
Hibernate(英文意思是冬眠,大概是说自从有了Hibernate后就可自此让JDBC“冬眠”
了吧)(www.hibernate.org)是一个非常流行的开源的易于配置和运行的基于 JDBC 的对
象-关系映射(JORM) 引擎。它提供了很丰富的功能,包括但不局限于下列功能:
?? 多种映射策略
?? 可迁移的持久化
?? 单个对象映射到多个表
?? 支持集合
?? 多态关联
?? 可自定义的 SQL 查询
Hibernate 使用 Java 编写, 是一个高度可配置的软件包, 可以通过两种配置文件格
式来进行配置。第一种配置文件名字为hibernate.cfg.xml。在启动时, Hibernate 查
询这个 XML 里面的属性来进行操作, 例如数据库连接字符串和密码, 数据库方言
MyEclipse 6 Java 开发中文教程
99 刘长炯著
(database dialect), 以及映射文件位置等。 Hibernate 在类路径中查找这个文件。第二
种配置文件是映射描述文件(文件扩展名为*.hbm.xml), 它将告诉 Hibernate 如何来将特
定的 Java 类和一个或者多个数据库表格中的数据进行映射。 MyEclipse 提供了工具来处
理这两种配置文件,并且可以将它们和你对数据库和 Hibernate 映射的 Java 类的修改进
行同步。
Hibernate 可以用在任何需要将 Java 对象和数据库表格中的数据进行操作的 Java
应用中。因此, 它在开发两层和三层的 J2EE 应用中很有用。向你的应用中集成
Hibernate 包括:
?? 向你的项目中安装 Hibernate 核心类和依赖的 JAR 类库
?? 创建 hibernate.cfg.xml 文件来描述如何访问数据库
?? 为每个持久化 Java 类创建单独的映射描述文件
更多关于 Hibernate 的基本和高级特性, 或者如何使用 Hibernate 进行开发的信息,
请查看本章的 7.7 参考资料部分。
7.2.2 Hibernate 要点
本节内容不能代替学习 Hibernate 的书籍或者文档,仅仅为了方便对本章内容的理解。
您可以现在先跳过本节的内容来阅读后续的操作部分。
1. 三W What, Why, When 什么是, 为什么, 什么时候
what:是一个 OR Mappming(O Object 对象 R relative 关系数据库映射) 框架
(framework)
why:
把一个对象的人 分解成一张横的表的几列
Person => id, name, age
根据对象自动生成对应的关系数据库的 SQL, 可以简化 Java 数据库开发, 代替
JDBC 来实现持久化(Persistence).
when:
1) 必须有明确的对象设计的时候才能用 Hibernate.
2) 数据库是用大量的存储过程实现的不能用 Hibernate !!!
3) 如果多表查询, 也要慎重 Hibernate, 查询顺序不可预料(createSQLQuery(手写查
询语句))
2. 怎么用
1) 全局配置信息被 Configuration 类解析(配置解析器)
a) 怎么连接数据库
hibernate.cfg.xml
获取数据库连接的参数(URL, username, password)
方言 (Dialect) 为了对不同的数据库生成相应的 SQL
b) 有那些映射文件需要处理
Configuration 类
Configuration 类负责管理 Hibernate 的配置信息。Hibernate 运行时需要
MyEclipse 6 Java 开发中文教程
100 刘长炯著
获取一些底层实现的基本信息,其中几个关键属性包括:
数据库 URL
数据库用户
数据库用户密码
数据库 JDBC 驱动类
数据库 dialect,用于对特定数据库提供支持,其中包含了针对特定数据库特性
的实现,如 Hibernate 数据类型到特定数据库数据类型的映射等。
使用 Hibernate 必须首先提供这些基础信息以完成初始化工作,为后继操作做好准
备。这些属性在hibernate 配置文件(hibernate.cfg.xml )中加以设定.
调用:
Configuration config = new Configuration().configure();
时,Hibernate 会自动在当前的 CLASSPATH 中搜寻 hibernate.cfg.xml 文件并将其读
取到内存中作为后继操作的基础配置。Configuration 类一般只有在获取 SessionFactory
时需要涉及,当获取 SessionFactory 之后,由于配置信息已经由 Hibernate 维护并绑定
在返回的 SessionFactory 之上,因此一般情况下无需再对其进行操作。
示例的配置文件:
<?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>
<!-- 显示后台的 SQL, 便于调试 -->
<property name="show_sql">true</property>
<property name="connection.username">classiccars</property>
<property name="connection.url">
jdbc:derby://localhost:1527/myeclipse;create=true
</property>
<property name="dialect">org.hibernate.dialect.DerbyDialect</property>
<property
name="connection.driver_class">org.apache.derby.jdbc.ClientDriver</property>
<property name="connection.password">myeclipse</property>
<!-- 实体映射文件 -->
<mapping resource="dao/User.hbm.xml" />
</session-factory>
2) 实体类(POJO Plain and Old Java Ojbect) JavaBean 的要求
值对象, 只有 getter, setter, 没有业务方法
MyEclipse 6 Java 开发中文教程
101 刘长炯著
什么样的对象需要映射
public class User implements java.io.Serializable {
private int id;
private String username;
getxxx
setxxx
}
a) 要有主键字段.
b) 可序列化(缓存, 有时候在内存, 有时候放硬盘)
3) 实体映射文件实体名.hbm.xml
告诉 Hibernate 怎么来做对象映射. 向哪个表插入数据, 每个属性的数据类型, 以及
对应数据表里的列名.
一个文件配置多个实体类也是可以的, 一般来说是一个实体一个配置文件.
<?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>
<class name="dao.User" table="users(数据库表格)" catalog="数据库名字">
<!-- 主键字段配置, hibernate 为我们生成主键id, 必须定义-->
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="increment" />
<!-- increment 是先从数据库取最大ID 然后加1, 再存入数据库
assigned 必须手工赋值给一个 ID
auto, identify, sequence, native, uuid.hex, hilo 等等
-->
</id>
<!-- property 默认把类的变量映射为相同名字的表列,当然我们可以修改其映射方
式-->
<!-- 类型写法两种 Hibernate type: string, int; Java 类的全名: java.lang.Integer
-->
<property name="username" type="java.lang.String">
<!-- 指定对应数据库中的字段信息 -->
<column name="username" length="200" not-null="true" />
</property>
<property name="password" type="java.lang.String">
<column name="password" length="20" not-null="true" />
</property>
<property name="age" type="java.lang.Integer">
MyEclipse 6 Java 开发中文教程
102 刘长炯著
<column name="age" />
</property>
</class>
</hibernate-mapping>
多对多几乎没人用, 一对一常用, 一对多比较常用.
3. SessionFactory 和 Session
SessionFactory ==> 等价于 DriverManager, 只需要一个.
SessionFactory 负责创建 Session 实例。可以通过 Configuation 实例构建
SessionFactory:
SessionFactory sessionFactory = config.buildSessionFactory();
Configuration 实例config 会根据当前的配置信息,构造 SessionFactory 实例并返回。
SessionFactory 一旦构造完毕,即被赋予特定的配置信息。也就是说,之后 config 的任
何变更将不会影响到已经创建的 SessionFactory 实例(sessionFactory)。如果需要
使用基于改动后的 config 实例的 SessionFactory,需要从 config 重新构建一个
SessionFactory 实例。
Session ==> 等价于 JDBC 中的 Connection
Session 实例通过SessionFactory 实例构建:
Session session = sessionFactory.openSession();
完整示例代码:
// 0. 加载配置和驱动等, 生成 Session 工厂(相当于连接池或者 DriverManager)
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
// 1. 打开 session
Session session = sessionFactory.openSession();
// 2. 打开事务(Transaction)
org.hibernate.Transaction tran = session.beginTransaction();
// 3. 生成实体类
User bean = new User();
// 4. 给 bean 赋值
bean.setUsername("zhangsan");
// 5. 保存或者更新(并没有立即保存到数据)
session.save(bean);
// 6. 提交事务(真正的保存或者更新数据)
tran.commit();
// 7. 做查询, 首先创建查询对象
String queryString = "from User";// HSQL 操作的是实体, 不是数据库表格
MyEclipse 6 Java 开发中文教程
103 刘长炯著
Query query = getSession().createQuery(queryString);
// 8. 读取查询结果
java.util.List<User> result = query.list();
4. Transaction 事务
Hibernate 是 JDBC 的轻量级封装,本身并不具备事务管理能力。在事务管理层,
Hibernate 将其委托给底层的JDBC 或者JTA,以实现事务管理和调度功能。
Hibernate 的默认事务处理机制基于JDBC Transaction。我们也可以通过配置文定采用
JTA 作为事务管理实现:
<property name="hibernate.transaction.factory_class">
net.sf.hibernate.transaction.JTATransactionFactory
<!--net.sf.hibernate.transaction.JDBCTransactionFactory-->
</property>
将事务管理委托给 JDBC 进行处理无疑是最简单的实现方式,Hibernate 对于 JDBC
事务的封装也极为简单。
我们来看下面这段代码:
session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
……
tx.commit();
从JDBC 层面而言,上面的代码实际上对应着:
Connection dbconn = getConnection();
dbconn.setAutoCommit(false);
……
dbconn.commit();
就是这么简单,Hibernate 并没有做更多的事情(实际上也没法做更多的事情),只
是将这样的 JDBC 代码进行了封装而已。
5. 查询对象 Query(Hibernate 参考文档的第11 章) 和 HSQL
普通查询如上所示
带参数的查询(相当于 PreparedStatement)
q = session.createQuery("from User u where u.name= :name");
q.setString("name", "张三");
q = session.createQuery("from User u where u.name= ?");
q.setString(0, "张三");
Native SQL 方式(用于特定的数据库的查询)
List cats=session.createSQLQuery(
"SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
"cat",
MyEclipse 6 Java 开发中文教程
104 刘长炯著
Cat.class
).list();
Listcat s=session.createSQLQuery(
"SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex},"+
"{cat}.MATE AS{cat.mate}, {cat}.SUBCLASSAS {cat.class},..."+
"FROM CAT {cat} WHERE ROWNUM<10",
"cat",
Cat.class
).list();
过滤重复记录:
select distinct cat.name from Cat cat
6. 一对多, 多对一, 多对多中的懒惰加载 Lazy, Fetch
新版本的 Hibernate 在处理Session 的时候已经内置了延迟加载机制,只有在真正发生数
据库操作的时候,才会从数据库连接池获取数据库连接. 这会带来问题.
例如一对多:
<set
name="addresses"
table="t_address"
lazy="false"
inverse="false"
cascade="all"
>
... <one-to-many
class="org.hibernate.sample.TAddress"
/>
</set>
示例代码:
Query q = getSession().createQuery("from User");
List userList = q.list();
TUser user =(TUser)userList.get(0);
System.out.println("User name => "+user.getName());
Set hset = user.getAddresses();
session.close();//关闭Session
TAddress addr = (TAddress)hset.toArray()[0];
System.out.println(addr.getAddress());
运行时抛出异常:
LazyInitializationException - Failed to lazily initialize a collection - no session or session
was closed
如果我们稍做调整,将session.close 放在代码末尾,则不会发生这样的问题。但是一般 DAO
执行结束后即关闭了 session.
这意味着,只有我们实际加载user 关联的address 时,Hibernate 才试图通过
session 从数据库中加载实际的数据集,而由于我们读取address 之前已经关闭了
MyEclipse 6 Java 开发中文教程
105 刘长炯著
session,所以报出session 已关闭的错误。
解决办法:
1) Hibernate.initialize 方法可以通过强制加载关联对象实现这一功能:
Hibernate.initialize(user.getAddresses());
session.close();
2) 用 HQL 里面的 fetch
from User fetch all properties
3) lazy="false"
7. Hibernate 分页
int currentPage = 0;// 当前页
int pageSize = 10;// 显示记录数
String queryString = "from User";
Query queryObject = getSession().createQuery(queryString);
// 设置从哪里读
queryObject.setFirstResult((currentPage - 1) * pageSize);
// 设置一共读几行
queryObject.