资源描述
北京魔乐科技软件学院 联系电话:010-51283346
1、课程名称:Hibernate
2、知识点
2.1、上次课程的主要知识点
2.2、本次预计讲解的知识点
3、具体内容
3.1、Hibernate框架和ORMapping
ORMapping是将对象与表建立映射关系,通过操作对象从而影响到数据库表中数据的开发模式。
Hibernate就是实现ORMapping的一个框架,除了Hibernate以外还有一些框架可以实现ORMapping功能。
优点
缺点
JDBC
执行速度快,执行的是SQL语句
编写速度慢,重复代码比较多。
EJB1、2
提出了ORMapping的概念
并提供了TO(Transfer Object)的概念,与vo作用类似,都是用来描述表中的数据的,但多实现了一个序列化接口。
设计不合理,无法使用。
JDO
简单直接
连接不容易管理。
Apache OJB
无
无法单独使用,必须结合其他框架一起使用,结合的最好的是JDO。
而且官方没有提供比较完善的开发文档
iBATIS/MyBATIS
比Hibernate执行速度快,
比JDBC开发速度快。
比Hibernate开发速度慢,
比JDBC执行速度慢。
Hibernate
成熟,下载量大,有开发工具支持
执行效率低
EJB3
使用了Hibernate3的源代码。
使用了Annotation配置,替代了XML,使配置文件简化,提高执行效率。
还是采用分布式开发,因此架构仍然比较复杂,成本高。
这里的ORMapping指的是 vo 与 table表 的映射关系。
vo在开发ORMapping时有其他的称呼:TO(EJB2),POJO(Hibernate中,Plain Olds Java Object,经典Java对象,只包含属性和getter,setter方法的对象。)
映射时,vo与表的关系可以通过两种方式来配置:
1) XML:传统方式,iBATIS和Hibernate使用该方式
a) 优点:配置文件单独存在,方便进行查看和维护。
2) Annotation:JDK1.5后提供的新方式,MyBATIS,hibernate3.2以上,EJB3支持。
a) 优点:执行速度比XML快。
这样在开发Hibernate时,每多一个vo(pojo)对象,就需要建立一个xml文件来描述这个vo与表的关系,这个XML称为映射文件,一般命名为: 类名.hbm.xml。
除了映射文件外,hibernate还会包含一个自己的核心配置文件,名称为:hibernate.cfg.xml
开发Hibernate时,不再使用JDBC中提供的Connection,PreparedStatement,ResultSet。而是改为Hibernate提供的替代类。
Connection à Session
PreparedStatement à Query
ResultSet à List
3.2、使用Hibernate完成基本CRUD功能
建立项目,并加入Hibernate支持。
建议在加入支持前,先配置一个数据库连接。
打开数据库管理界面。
在这个DB Browser中建立一个新的数据库连接。
填写内容后,点Test Driver测试
如果成功,则表示连接配置完成。
配置好后,如果可以看到以下内容,则表示配置成功。
下面就可以开始为项目加入Hibernate支持。
加入支持时,注意将支持包加入到lib目录下。
配置Hibernate核心配置文件。
选择之前配置好的连接,加入到这里。
建立一个新的包,并自动生成HibernateSessionFactory类,该类的功能与JDBC中的DataBaseConnection的作用相同。
在生成的核心配置文件中,包含了三部分配置:
1) 左边:数据库连接。
2) 右上:属性配置,可以对hibernate执行过程的一些属性进行调整,例如:
a) 显示执行的sql语句。
b) 格式化显示的sql语句。
3) 右下:所有映射文件所在的位置。
生成的HibernateSessionFactory类可以提供打开连接和关闭连接的功能,而且提供的连接是支持数据源连接池的。
package cn.mldn.hibernate.dbc;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
public class HibernateSessionFactory {
// 配置文件的位置和文件名
private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
// 用来实现数据源功能的核心类。类似一个Map,key保存访问线程,value保存数据库连接对象Session
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
// 用来读取核心配置文件,并进行解析
private static Configuration configuration = new Configuration();
// 创建Session对象
private static org.hibernate.SessionFactory sessionFactory;
private static String configFile = CONFIG_FILE_LOCATION;
static {
try {
configuration.configure(configFile);
// 通过读取的配置文件,搭建连接池。
sessionFactory = configuration.buildSessionFactory();
} catch (Exception e) {
System.err.println("%%%% Error Creating SessionFactory %%%%");
e.printStackTrace();
}
}
private HibernateSessionFactory() {
}
/**
* 取得数据库连接
* @return Session
* @throws HibernateException
*/
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = sessionFactory.openSession();
threadLocal.set(session);
}
return session;
}
public static void rebuildSessionFactory() {
try {
configuration.configure(configFile);
sessionFactory = configuration.buildSessionFactory();
} catch (Exception e) {
System.err.println("%%%% Error Creating SessionFactory %%%%");
e.printStackTrace();
}
}
/**
* 关闭一个连接
* @throws HibernateException
*/
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
// 清空当前线程使用的Session
threadLocal.set(null);
// 将连接放回连接池
if (session != null) {
session.close();
}
}
public static org.hibernate.SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void setConfigFile(String configFile) {
HibernateSessionFactory.configFile = configFile;
sessionFactory = null;
}
public static Configuration getConfiguration() {
return configuration;
}
}
重点掌握连接池的实现方式,以及数据库连接的取得和关闭方法。
下面开始编写pojo,不需要直接手工建立类,而是通过表生成。
在要建立pojo的表上选择生成映射。
对于表中的主键值,有很多种生成方式,常用的有:
1) native:自动增长,通用的形式。
2) assigned:程序添加。
3) increment:mysql,sqlserver,db2的自增长。
4) sequence:oracle的自增长
5) foreign:主键同时是外键,一对一关系时使用。
6) uuid:随机生成主键值。可以选择uuid.hex(16位)或uuid.string(128位)
完成
选择No,不需要切换到Hibernate映射界面。
package cn.mldn.hibernate.pojo;
import java.sql.Timestamp;
public class News implements java.io.Serializable {
private Integer nid;
private String title;
private String content;
private Timestamp postDate;
private Integer type;
public News() {
}
public News(String title, String content, Timestamp postDate, Integer type) {
this.title = title;
this.content = content;
this.postDate = postDate;
this.type = type;
}
public Integer getNid() {
return this.nid;
}
public void setNid(Integer nid) {
this.nid = nid;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return this.content;
}
public void setContent(String content) {
this.content = content;
}
public Timestamp getPostDate() {
return this.postDate;
}
public void setPostDate(Timestamp postDate) {
this.postDate = postDate;
}
public Integer getType() {
return this.type;
}
public void setType(Integer type) {
this.type = type;
}
}
映射文件。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"
<hibernate-mapping>
<!--
News类和test库中的news表存在映射关系。
-->
<class name="cn.mldn.hibernate.pojo.News" table="news" catalog="test">
<!--
类中的Integer类型的nid属性,与表中的主键nid对应,主键的生成方式是native
-->
<id name="nid" type="java.lang.Integer">
<column name="nid" />
<generator class="native" />
</id>
<!--
String类型的title与表中的title字段对应,长度最大为200,不允许为空
-->
<property name="title" type="java.lang.String">
<column name="title" length="200" not-null="true" />
</property>
<property name="content" type="java.lang.String">
<column name="content" length="65535" not-null="true" />
</property>
<property name="postDate" type="java.sql.Timestamp">
<column name="post_date" length="19" not-null="true" />
</property>
<property name="type" type="java.lang.Integer">
<column name="type" not-null="true" />
</property>
</class>
</hibernate-mapping>
要求能看懂映射文件中的每个字段的映射,而且可以手工添加新字段的映射。
建立接口与实现类(DAO)
package cn.mldn.hibernate.dao;
import cn.mldn.hibernate.pojo.News;
public interface NewsDAO extends DAO<News, Integer> {
}
注意实现类中所有操作都要使用Session和Query对象实现。
package cn.mldn.hibernate.dao.impl;
import java.util.List;
import org.hibernate.Query;
import cn.mldn.hibernate.dao.NewsDAO;
import cn.mldn.hibernate.dbc.HibernateSessionFactory;
import cn.mldn.hibernate.pojo.News;
public class NewsDAOImpl implements NewsDAO {
public boolean doCreate(News vo) throws Exception {
HibernateSessionFactory.getSession().save(vo);
return true;
}
public boolean doRemove(Integer id) throws Exception {
// 删除时要求传入要删除的对象,因此先查询再删除
// HibernateSessionFactory.getSession().delete(this.findById(id));
// 如果不想先查再删,可以使用HQL语句来处理。
// Hibernate Query Language是使用类来进行数据库查询和修改等操作,而不是表
String hql = "DELETE FROM News AS n WHERE n.nid = ?";
Query query = HibernateSessionFactory.getSession().createQuery(hql);
// 设置参数,注意下标从0开始。
query.setInteger(0, id);
if (query.executeUpdate() > 0) {
return true;
}
return true;
}
public boolean doUpdate(News vo) throws Exception {
// 这个修改是修改表中的所有字段
HibernateSessionFactory.getSession().update(vo);
// 如果要单独修改某一个字段,使用HQL语句。
// String hql = "UPDATE News AS n SET n.type = ? WHERE n.nid = ?" ;
return true;
}
public List<News> findAll(int cp, int ls, String paramKey, String paramValue)
throws Exception {
String hql = "FROM News AS n WHERE n." + paramKey + " LIKE ?";
Query query = HibernateSessionFactory.getSession().createQuery(hql);
query.setString(0, "%" + paramValue + "%");
// 设置分页条件,不管用什么数据库,分页都用下面的方法实现。
query.setFirstResult((cp - 1) * ls);
query.setMaxResults(ls);
return query.list();
}
public List<News> findAll() throws Exception {
// 使用HQL
String hql = "FROM News";
Query query = HibernateSessionFactory.getSession().createQuery(hql);
// query.list相当于pstmt.executeQuery(),但返回的直接是一个List集合。
return query.list();
}
public News findById(Integer id) throws Exception {
return (News) HibernateSessionFactory.getSession().get(News.class, id);
}
public int getCount(String paramKey, String paramValue) throws Exception {
String hql = "SELECT count(n) FROM News AS n WHERE n." + paramKey
+ " LIKE ?";
Query query = HibernateSessionFactory.getSession().createQuery(hql);
query.setString(0, "%" + paramValue + "%");
return (Integer) query.uniqueResult();
}
}
工厂类
package cn.mldn.hibernate.factory;
import cn.mldn.hibernate.dao.NewsDAO;
import cn.mldn.hibernate.dao.impl.NewsDAOImpl;
public class DAOFactory {
public static NewsDAO getNewsDAOInstance() {
return new NewsDAOImpl();
}
}
不需要再传参
处理服务类
package cn.mldn.hibernate.service;
import java.util.Map;
import cn.mldn.hibernate.pojo.News;
public interface NewsService {
public boolean insert(News news);
public boolean update(News news);
public Map list(int cp,int ls,String paramKey,String paramValue);
}
实现类
package cn.mldn.hibernate.service.impl;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.Transaction;
import cn.mldn.hibernate.dao.NewsDAO;
import cn.mldn.hibernate.dbc.HibernateSessionFactory;
import cn.mldn.hibernate.factory.DAOFactory;
import cn.mldn.hibernate.pojo.News;
import cn.mldn.hibernate.service.NewsService;
public class NewsServiceImpl implements NewsService {
private NewsDAO newsdao;
public NewsServiceImpl() {
newsdao = DAOFactory.getNewsDAOInstance();
}
public boolean insert(News news) {
boolean flag = false;
// 事务处理这里需要手工完成。这里需要手工完成事务的提交和回滚功能
Transaction tx = HibernateSessionFactory.getSession()
.beginTransaction();
try {
flag = this.newsdao.doCreate(news);
// 提交
mit();
} catch (Exception e) {
e.printStackTrace();
// 回滚
tx.rollback();
} finally {
HibernateSessionFactory.closeSession();
}
return flag;
}
public Map list(int cp, int ls, String paramKey, String paramValue) {
Map map = new HashMap();
try {
map.put("allNews", this.newsdao.findAll(cp, ls, paramKey,
paramValue));
map.put("count", this.newsdao.getCount(paramKey, paramValue));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
HibernateSessionFactory.closeSession();
}
return map;
}
public boolean update(News news) {
boolean flag = false;
Transaction tx = HibernateSessionFactory.getSession()
.beginTransaction();
try {
flag = this.newsdao.doUpdate(news);
mit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
} finally {
HibernateSessionFactory.closeSession();
}
return flag;
}
}
一定注意,添加,修改和删除操作时,必须手工提交事务和回滚事务。
工厂类。
package cn.mldn.hibernate.factory;
import cn.mldn.hibernate.service.NewsService;
import cn.mldn.hibernate.service.impl.NewsServiceImpl;
public class ServiceFactory {
public static NewsService getNewsServiceInstance() {
return new NewsServiceImpl();
}
}
测试时发现查询全部记录数的代码有错误,类型转换异常,需要修改返回部分的代码。
public int getCount(String paramKey, String paramValue) throws Exception {
String hql = "SELECT count(n) FROM News AS n WHERE n." + paramKey
+ " LIKE ?";
Query query = HibernateSessionFactory.getSession().createQuery(hql);
query.setString(0, "%" + paramValue + "%");
// 在执行hql的count语句时,返回类型是long而不是int。
return ((Long) query.uniqueResult()).intValue();
}
在这些CRUD功能中,用到了以下5个支持类:
1) Configuration:读取核心配置文件
2) SessionFactory:创建Session
3) Session:相当于Connection,但多了CRUD方法
a) save():添加
b) update():修改
c) delete():删除
d) get():按主键查询。
4) Query:相当于PreparedStatement,但使用的是HQL语句
a) setXxxxx():设置?参数,下标从0开始
b) executeUpdate():执行修改或删除。
c) list():查询多条数据
d) uniqueResult():查询一条数据
e) setFirstReesult():设置开始记录数((cp-1) *ls)
f) setMaxResults():设置每页显示记录数(ls)
5) Transaction:处理事务,通过session.beginTransaction()开始
a) commit()
b) rollback()
3.3、Hibernate中的查询
Hibernate中包含的查询方式:
1) Session查询:使用session.get()或load()方法,根据主键进行查询
a) get和load方法的区别:
i. 当没有查询到数据时,get方法返回null,load方法直接提示异常。
ii. load方法查询对象使用懒汉式,get方法是饿汉式。因此使用load方法时,连接不能随意关闭。
iii. 因此开发中需要使用get方法。
2) Query(HQL)查询:根据HQL语句进行查询
3) Criteria(Query By Criteria )查询:根据Hibernate中提供的条件语句来进行查询。
使用load方法查询时,可能会提示这个错误
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
HQL查询使用的是Query对象,其中HQL语句的语法与SQL语法基本一致。
区别:
1) HQL查询的是类和类中的属性,而SQL则是表和表中的字段,因此注意HQL语句中类名和属性名的大小写。
2) HQL只支持修改,删除和查询,不支持添加功能。
3) hql修改和删除时的语法与sql相同
4) 查询部分有区别:
a) hql可以不写SELECT,直接FROM某个类。这样查询的结果就是该类的List集合
b) 如果写了SELECT,根据SELECT后的内容,返回的List集合中的类型也不同。
i. SELECT后只有一个属性/字段。则集合中的类型与该属性的类型一致。例如:
public List test() throws Exception {
String hql = "SELECT n.postDate FROM News AS n ";
Query query = HibernateSessionFactory.getSession().createQuery(hql);
return query.list();
}
返回的集合中的类型就是postDate的类型:Timestamp
ii. 如果SELECT后写了多个属性/字段,集合中会保存Object数组
public List test() throws Exception {
String hql = "SELECT n.nid,n.title,n.content FROM News AS n ";
Query query = HibernateSessionFactory.getSession().createQuery(hql);
return query.list();
}
测试时,可以发现返回的集合中保存的类型是Object[],数组长度为3,与查询的字段数(属性)一致
iii. 如果要使用这种查询多个属性的写法,还需要手工将Object[]转换为pojo对象。
List all = ServiceFactory.getNewsServiceInstance().test();
Iterator iter = all.iterator();
List allNews = null;
while (iter.hasNext()) {
if (allNews == null) {
allNews = new ArrayList();
}
Object[] objs = (Object[]) iter.next();
News news = new News();
news.setNid((Integer) objs[0]);
news.setTitle((String) objs[1]);
news.setContent((String) objs[2]);
allNews.add(news);
}
iv. 由于上面的处理方法比较复杂,因此Hibernate在3.2版本后提供了自动转换成pojo的功能。
public List test() throws Exception {
String hql = "SELECT n.nid AS nid,n.title AS title,n.content AS content FROM News AS n ";
Query query = HibernateSessionFactory.getSession().createQuery(hql);
// 根据别名将结果转换为pojo
query.setResultTransformer(new AliasToBean
展开阅读全文