1 Hibernate核心技术
1.1 理论基础
操作数据库的3个阶段
1、 操作JDBC阶段
2、 封装JDBC
3、 ORM阶段
ORM对象关系映射
持久层概念
1.2 Hibernate开发流程
步骤
1、 创建Hibernate的配置文件:该文件负责初始化Hibernate配置,包括数据库配置和映射文件配置
2、 创建Hibernate映射文件:每一个数据表对应一个映射文件,该文件描述了数据库中表的信息,也描述了对应的持久化类的信息
3、 创建持久化类
以上三步是开发Hibernate要实现的关键内容,接下来就要面向Web应用层进行编码
1、 编写DAO层:通过HibernateAPI编写访问数据库的代码
2、 编写Service层:编写业务层实现,调用DAO层类代码
下载并安装Hibernate
必需的包:
Hibernate3.jar
cglib-2.1.jar、asm-attrs.jar、asm.jar:CGLIB库,用来实现PO字节码的动态生成
dom4j-1.5.2.jar:java的XMLAPI,类似于jdom,用来读写XML文件的
commons-collections-2.1.1.jar
commons-logging-1.0.4.jar、log4j-1.2.9.jar:包含了日志功能
将以上文件复制到lib目录下
1.3 Hibernate配置文件详解
有两种形式:一种是XML(hibernate.cfg.xml),一种是properties属性文件(hibernate.properties),一般使用XML
Etc子目录下提供了默认的Hibernate配置文件hibernate.cfg.xml,复制到src目录下,修改后如下
<hibernate-configuration> <session-factory name="foo"> <property name="myeclipse.connection.profile">JDBC for MySQL</property> <!-- 显示执行的SQL语句 --> <property name="show_sql">true</property> <property name="connection.url">jdbc:mysql://localhost:3306/demo</property> <property name="connection.username">root</property> <property name="connection.password"></property> <property name="connection.drive_class">org.gjt.mm.mysql.Driver</property> <!-- 选择使用的方言 --> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 映射文件 --> <mapping resource="com/demo/hibernate/beans/User.hbm.xml"/> </session-factory> </hibernate-configuration>
1.4 编写映射文件User.hbm.xml
Hibernate映射文件包含了对象/关系映射(O/R Mapping)所需的元数据。建立一个映射文件User.hbm.xml来映射到数据库表User,映射的类名为User,所在的包为com.demo.hibernate.beans。如下
<?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 package="com.demo.hibernate.beans"> <class name="User" table="user"> <id name="id" column="ID" type="integer"> <generator class="native"/> //设置主键生成方式 </id> <property name="username" column="username" type="string"/> <property name="password" column="password" type="string"/> <property name="email" column="email" type="string"/> </class> </hibernate-mapping>
1.4.1 根元素<hibernate-mapping>
每一个hbm.xml文件都有唯一的一个根元素。Package属性指定一个包前缀。<hibernate-mapping>元素允许嵌套多个<class>映射。但是最好的做法是一个持久化类对应一个映射文件,并以持久化的超类名称命名
1.4.2 使用<class>定义类
<class name="User" table="user">
Class也可以是一个接口,之后可以用<subclass>来指定该接口的实际实现类。
1.4.3 使用<id>定义主键
如果表使用联合主键,你可以映射多个属性为标识符属性。下面定义了两个字段作为联合主键。此时持久化类必须重载equals()和hashCode()方法,来实现组合标识符的相等判断。
<composite-id>
<key-property name="username"/>
<key-property name="password"/>
</composite-id>
1.4.4 <generator>设置主键生成方式
<generator class="native"/>
1.4.5 Hibernate映射类型
1.4.6 使用<property>定义属性
<property name="username" column="username" type="string"/>
1.4.7 <many-to-one>配置多对一映射
1.4.8 <one-to-one>配置一对一映射
1.4.9 Hibernate映射文件自动生成工具
1.5 编写持久化类User.java
持久化类是指其实例需要被Hibernate持久化到数据库中的类。Hibernate使用简单的Java对象(Plain Old Java Objects,就是POJOs,有时也称作Plain Ordinary Java Objects)这种编程模型来进行持久化
package com.demo.hibernate.beans; public class User { private java.lang.Integer id; private String username; private String password; private String email; public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } …… }
getXXX()和setXXX()方法必须符合特定的命名规则,“get”和“set”后面紧跟属性的名字,并且属性名的首字母为大写。
如果持久化类的属性为boolean类型,那么它的get方法即可以用get为前缀,也可以用“is”为前缀。
1.6 编写辅助类HibernateSessionFactory.java
Session是一个持久化管理器,我们通过它来从数据加中存取User。首先,我们要从SessionFactory中获取一个Session。通过对configure()调用来装载hibernate.cfg.xml配置文件,并初始化成一个Configuration实例。
通常SessionFactory只是被初始化一次,例如通过一个load-on-startup servlet来初始化。这意味着你不应该在servlet中把它作为一个实例变量来持有,而应该放在其他地方。进一步说,我们需要使用单例(Singleton)模式,才能更容易地在程序中访问SessionFactory。下面的方法就同时解决了这两个问题
package com.demo.hibernate.util; import org.hibernate.cfg.Configuration; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; public class HibernateSessionFactory { private static String CONFIG_FILE_LOCATION="/hibernate.cfg.xml"; private static final ThreadLocal threadLocal=new ThreadLocal(); private static final Configuration cfg=new Configuration(); private static SessionFactory sessionFactory; //取得session public static Session currentSession() throws HibernateException{ Session session=(Session)threadLocal.get(); if(session==null){ if(sessionFactory==null){ try{ cfg.configure(CONFIG_FILE_LOCATION); sessionFactory=cfg.buildSessionFactory(); }catch(Exception e){ System.err.println("%%%%Error Creating SessionFactory%%%%"); e.printStackTrace(); } } session=sessionFactory.openSession(); threadLocal.set(session); } return session; } //关闭Session对象 public static void closeSession() throws HibernateException{ Session session=(Session)threadLocal.get(); threadLocal.set(null); if(session==null){ session.close(); } } }
这个类不但在它的静态初始器中使用了SessionFactory,还使用了一个ThreadLocal变量来保存Session作为当前工作线程。
1.7 编写DAO类UserDAO.java
所谓的DAO(Data Access Object)层,就是数据访问接口,为了基于Hibernate的开发中,通常将业务层与数据层分开,DAO层只负责调用Hibernate API实现CRUD操作,Service层面向用户负责调用DAO层的代码。这样做的好处是,数据层的代码不用关心业务,可以更好地实现移植
下面编写一个DAO类UserDAO.java,该类实现一个getUser()函数,根据用户名username查询一个用户对象。
public class UserDAO { public User getUser(String username)throws HibernateException{ Session session=null; Transaction tx=null; User user=null; try{ session=HibernateSessionFactory.currentSession(); tx=session.beginTransaction(); Query query=session.createQuery("from User where username=?"); query.setString(0, username.trim()); user=(User)query.uniqueResult(); query=null; tx.commit(); }catch(HibernateException e){ throw e; }finally{ if(tx!=null){ tx.rollback(); } HibernateSessionFactory.closeSession(); } return user; } }
1.8 编写Service类并运行
即服务层,就是面向用户服务,它定义的方法都是与实际的业务相关的。
下面定义一个Service类UserService,它有一个函数valid(),根据用户名和密码来判断用户是否存在。
运行出错
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:645) at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:247) at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:284) at javax.naming.InitialContext.getNameParser(InitialContext.java:439) at org.hibernate.util.NamingHelper.bind(NamingHelper.java:52) at org.hibernate.impl.SessionFactoryObjectFactory.addInstance(SessionFactoryObjectFactory.java:90) at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:306) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1294) at com.demo.hibernate.util.HibernateSessionFactory.currentSession(HibernateSessionFactory.java:19) at com.demo.hibernate.dao.UserDAO.getUser(UserDAO.java:16) at com.demo.hibernate.service.UserService.valid(UserService.java:8) at com.demo.hibernate.service.UserService.main(UserService.java:19)
原因:配置文件在存在<session-factory name="foo">,将name="foo"去掉即可
1.9 Hibernate核心类用法
1.9.1 使用Configuration装载配置
使用hibernate.cfg.xml,该文件中设置了数据库连接的属性和hbm.xml映射文件配置,Hibernate会自动加载该配置属性,并自动找到POJO类。
Configuration cfg=new Configuration();
Cfg.configure("hibernate.cfg.xml");
1.9.2 sessionFatcory
sessionFaction=cfg.buildSessionFactory();
1.9.3 Session
主要功能是提供对映射的实体类实例的创建、读取和删除操作。实例可能以下面3种状态存在。
自由状态(transient):不曾进行持久化,未与任何Session相关联
持久化状态(persistent):仅与一个Session相关联
游离状态(detached):已经进行过持久化,但当前未与任何Session相关联
1、 Save()保存对象
用于将一个新实例化的对象持久化,保存到数据库中
如:session.save(user);
2、 Load()装载对象
如果你知道某个实例的持久化标识,就可以使用Session的load()方法来获取它。
如:User user=(User)session.load(User.class,new Integer("1"));
表示加载标识符为1的User,并赋给一个新的实例变量user。
此外,你可以把数据加载到指定的对象实例上(覆盖该实例原来的数据)
Session.load(user,new Integer("1"));(如果没有匹配的数据库记录,load方法可能抛出无法恢复的异常unrecoverable exception)
3、 Get()装载对象
如果你不确定是否有匹配的行记录存在,应该使用get()方法,它会立刻访问数据库,如果没有,会返回null。
4、 Flush()强制提交刷新
如果一个实例被更改了,则可以使用flush()将更改后的实例对象强制保存到数据库中。
5、 Update()提交游离状态的对象
6、 Delete()移除持久化对象
该函数用于从数据库中删除对象对应的记录
7、 Refresh()强制装载对象
任何时候都可以使用refresh()方法强迫装载对象和它的集合。如果你使用数据库触发器功能来处理对象的某些属性,这个方法就很有用了。
1.9.4 使用Transaction管理事务
Transaction tx=session.begionTransaction();
…
Tx.commit();
1.9.5 使用Query进行HQL查询
1、 不带参数的查询
Query query=session.createQuery(“from user”);
2、 带参数的查询
接口Query提供了对命名参数、JDBC风格的问号(?)参数两种参数绑定方法。
如:
Query query=session.createQuery("from User where username=:username");
query.setString("username","admin")
也可以使用集合型参数。如下所示,在SQL语句中使用in关键字指向一个命名参数列表
List names=new ArrayList();
names.add(admin);
names.add(test);
Query query=session.createQuery("from user where username in (:nameList)");
query.setParameterList("nameList",names);
3、 使用问号参数
Query query=session.createQuery("from user where username=?");
query.setString(0,"admin");
4、 取得List结果集
List list=query.list();
5、 取得迭代列表结果集
Iterate()
6、 取得一个对象
如果你知道当前查询只会返回一个对象,则可使用list()的快捷方式uniqueResult()来取得一个对象
User user=(User)query.uniqueResult()
7、 标量查询
查询可以在select从句中指定类的属性,甚至可以调用SQL统计函数
Iterator results=session.createQuery(
"Select user.username,count(user.email) from user group by username")
.list().iterator;
8、 分页查询
query.setFirstResult(10);//设置起始范围
query.setMaxResults(20);//设置结束范围
List list=query.list();
9、 创建SQL查询
可以使用createSQLQuery()方法,用普通的SQL来描述查询并由Hibernate处理将结果贪集转换成对象的工作。
List users=session.createSQLQuery(SELECT {user}.* FROM User {user}).list();
其中SQL别名需要用大括号包围起来。和Hibernate查询一样,SQL查询也可以包含命名参数和占位参数
1.9.6 使用Criteria进行条件查询
Criteria接口与Query接口非常类似,它允许你创建并执行面向对象的标准化查询。动态地使用面向对象API创建查询,而非在Java代码中嵌入字符串
创建Criteria实例
Cirteria criteria=session.createCriteria(User.class);
Criteria.setMaxResults(50);
List users=criteria.list();
1、 添加查询条件
查询条件是通过org.hibernate.criterion.Restrictions类来实现的,用来模拟SQL语句中的条件关键字,例如like、between、and、or等
Criteria criteria=session.createCriteria(User.class);
criteria.add(Restrictions.like("username","admin%"));
criteria.add(Restrictions.between("ID",1,10));
List users=criteria.list();
2、 添加排序条件
可以使用org.hibernate.criterion.Order来为查询结果排序,它共包含两个方法,asc()和desc(),分别实现SQL语句中的asc和desc关键字。通过调用Criteria的addOrder()来添加多个Order实例,如下
List users=session.createCriteria(User.class)
.add(Restrictions.like("username","admin%"))
.addOrder(Order.asc("username"))
.addOrder(Order.asc("password"))
.list();
3、 示例查询
Org.hibernate.criterion.Example类允许通过一个给定实例来构建一个条件查询。
User user=new User();
User.setUsername();
List results=session.createCriteria(User.class)
.add(Example.create(user)).list();