一、Hibernate介绍
之前使用过iBatis,虽然现在互联网行业使用Hibernate较少,但最近还是抽时间把Hibernate看了一下,出于对性能的考虑,Hibernate相比较而言最大的缺点就是性能的问题。其实这也是必然会出现的问题,任何框架对底层的包装都会产生这些性能问题,这个主要看我们对性能的要求到底到什么程度了。
关于DAO模式:业务层屏蔽了数据访问的底层(Data Accessor)实现,业务层(Domain Object)仅包含与本领域相关的逻辑对象和算法。
关于持久化:狭义的理解:Java对象存在于内存中,Hibernate将java对象持久化到关系型数据库中。广义的理解:保存,更新,删除,查询等操作都是持久化。确切的说,数据库中存放的是关系数据,而不是对象。我们说的”从数据库中加载对象“等说法,主要是站在hibernate客户端程序的角度来看待数据库访问操作的。客户程序可以假想数据库中存放的就是对象,只需要委托hibernate从数据库中去加载就行了,hibernate要做的就是把这些对象映射为数据库中的相应关系数据,大致流程如下:
1.运用java反射机制(Constuctor.newInstance()),获取到对象的class类型(要求持久化类必须要有一个无参的构造函数);
2.参考对象-关系映射,获取对象对应的数据表以及属性、字段的对应;
3.生成SQL语句并执行(hibernate query内置了HQL语言,语法和SQL相似,不过它是面向对象的);
二、Hibernate使用
0. 表结构如下:
sample.minfo (Table)
=====================
id: INT
mname: VARCHAR
mage: INT
mgender: VARCHAR
1.Hibernate的全局配置文件 hibernate.cfg.xml(两种配置形式,也可以配置成.properties)
<?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> <!-- SessionFactory --> <session-factory> <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3309/sample</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <property name="hibernate.show_sql">false</property> </session-factory> </hibernate-configuration>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://127.0.0.1:3309/sample
hibernate.connection.username=root
hibernate.connection.password=root
hibernate.show_sql=true
当通过Configuration的默认构造函数来创建实例的时候,hibernate会到classpath下查找默认的hibernate.properties文件,如果找到,就把它的配置信息加载到内存中,默认情况下hibernate不会加载hibernate.cfg.xml文件,必须通过Configuration的configure()方法来显示加载:
Configuration config = new Configuration().configure();
hibernate.show_sql=true //表示在控制台打印hibernate生成的sql语句
hibernate.dialect=org.hibernate.dialect.MySQLDialect //标示hibernate使用的SQL方言,这里是mySQL
2. hibernate持久化类 monkey.java
package HibernateTest; import java.util.StringTokenizer; public class Monkey { private int id; private String firstName; private String lastName; // private String monkeyName; private int monkeyAge; private String monkeyGender; private String anotherGender; public String getAnotherGender() { return anotherGender; } public void setAnotherGender(String anotherGender) { this.anotherGender = anotherGender; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMonkeyName() { // return monkeyName; return firstName + " " + lastName; } public void setMonkeyName(String monkeyName) { // this.monkeyName = monkeyName; StringTokenizer st = new StringTokenizer(monkeyName); firstName = st.nextToken(); lastName = st.nextToken(); } public int getMonkeyAge() { return monkeyAge; } public void setMonkeyAge(int monkeyAge) { this.monkeyAge = monkeyAge; } public String getMonkeyGender() { return monkeyGender; } public void setMonkeyGender(String monkeyGender) { this.monkeyGender = monkeyGender; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Monkey() { } }
3. 关系-对象配置文件 Monkey.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="org.hibernate.auction"> <class name="HibernateTest.Monkey" table="minfo" lazy="false"> <id name="id" column="id" type="int" > <generator class="increment"/> </id> <property name="monkeyName" column="mname" type="string" not-null="true" /> <property name="monkeyAge" column="mage" type="int" not-null="true" /> <property name="monkeyGender" column="mgender" type="string" not-null="true" /> <property name="anotherGender" formula=" (select mgender from minfo) " /> </class> </hibernate-mapping>
4.测试程序
package HibernateTest; import java.util.Iterator; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; public class HibernateService { public static SessionFactory sessionFactory; // 初始化Hibernate,创建SessionFactory实例 static { try { // 根据默认位置的Hibernate配置文件的配置信息,创建一个Configuration实例 Configuration config = new Configuration(); // 加载Monkey类的对象-关系映射文件 config.addClass(Monkey.class); // 创建SessionFactory实例 sessionFactory = config.buildSessionFactory(); } catch (RuntimeException e) { e.printStackTrace(); throw e; } } // 查询所有的Monkey对象,然后打印Monkey对象信息 public void findAllMonkeys() { Session session = sessionFactory.openSession();// 创建一个会话 Transaction tx = null; try { tx = session.beginTransaction();// 开始一个事物 Query query = session .createQuery(" from Monkey as m order by m.monkeyName asc"); @SuppressWarnings("unchecked") List<Monkey> monkeys = query.list(); Iterator<Monkey> it = monkeys.iterator(); while (it.hasNext()) { Monkey monkey = it.next(); System.out.println(monkey.getId() + "-" + monkey.getMonkeyName() + "-" + monkey.getMonkeyAge() + "-" + monkey.getMonkeyGender()+"-"+monkey.getAnotherGender()); } tx.commit(); } catch (RuntimeException e) { if (tx != null) tx.rollback(); e.printStackTrace(); throw e; } finally { session.close(); } } public void saveMonkey(Monkey monkey) { Session session = sessionFactory.openSession();// 创建一个会话 Transaction tx = null; try { tx = session.beginTransaction();// 开始一个事物 session.save(monkey); tx.commit(); } catch (RuntimeException e) { if (tx != null) tx.rollback(); e.printStackTrace(); throw e; } finally { session.close(); } } public void deleteMonkey(Monkey monkey) { Session session = sessionFactory.openSession();// 创建一个会话 Transaction tx = null; try { tx = session.beginTransaction();// 开始一个事物 monkey = (Monkey) session.get(Monkey.class, monkey.getId()); session.delete(monkey); tx.commit(); } catch (RuntimeException e) { if (tx != null) tx.rollback(); e.printStackTrace(); throw e; } finally { session.close(); } } public void updateMonkey(Monkey monkey,int age) { Session session = sessionFactory.openSession();// 创建一个会话 Transaction tx = null; try { tx = session.beginTransaction();// 开始一个事物 monkey = (Monkey) session.get(Monkey.class, monkey.getId()); monkey.setMonkeyAge(age); session.update(monkey); tx.commit(); } catch (RuntimeException e) { if (tx != null) tx.rollback(); e.printStackTrace(); throw e; } finally { session.close(); } } public static void main(String[] args) { HibernateService service = new HibernateService(); Monkey monkey = new Monkey(); //monkey.setId(2); //monkey.setMonkeyName("libin"); monkey.setFirstName("lee"); monkey.setLastName("nicholas"); monkey.setMonkeyAge(520); monkey.setMonkeyGender("M"); //service.saveMonkey(monkey); //service.deleteMonkey(monkey); //service.updateMonkey(monkey, 1314); service.findAllMonkeys(); } }
有几点需要说明的地方:
A. 我们可以配置
<property name="monkeyGender" column="mgender" access="field" type="string" not-null="true" />
access="field" 代表用hibernate使用反射机制直接访问持久化类的属性,而不是通过 getXXX 或 setXXX 方法
B. 如果持久化类中没有设置monkeyName属性也没关系,hibernate默认会直接访问setName 和 getName 方法,
例如上例我们就注释掉了// private String monkeyName; 因为我们有时候需要在这两个方法里写一下我们自己的逻辑,例如:
private String firstName; private String lastName; public String getMonkeyName() { // return monkeyName; return firstName + " " + lastName; } public void setMonkeyName(String monkeyName) { // this.monkeyName = monkeyName; StringTokenizer st = new StringTokenizer(monkeyName); firstName = st.nextToken(); lastName = st.nextToken(); }
有时候数据表里字段没有对应的firstName和lastName,我们需要组合一下然后属性配置还是配置monkeyName:
<property name="monkeyName" column="mname" type="string" not-null="true" />
因为如果持久化类中没有设置monkeyName属性也没关系,hibernate默认会直接访问setXXX 和 getXXX 方法;有时候我们可能需要加一些数据验证,外部输入调用持久化类的set方法时候验证数据合法性,但hibernate从数据库的读取一般都是合法的数据这个时候没必要验证,可以写成 access="field" 就解决了这个矛盾。
hibernate可以访问各种级别的getXXX setXXX 包括private,如果某个属性我们只希望hibernate去set就可以把这个属性设置为private;
C. formula 计算派生属性的值
有时候持久化类的属性需要通过计算才能得出,这种属性称为派生属性,我们可以如下配置:
private String anotherGender; public String getAnotherGender() { return anotherGender; } public void setAnotherGender(String anotherGender) { this.anotherGender = anotherGender; }
数据库表中并没有anotherGender属性,但我们可以在配置文件中直接配置得到:
<property name="anotherGender" formula=" (select mgender from minfo) " />
然后通过查询得出:
@SuppressWarnings("unchecked") List<Monkey> monkeys = query.list(); Iterator<Monkey> it = monkeys.iterator(); while (it.hasNext()) { Monkey monkey = it.next(); System.out.println(monkey.getId() + "-" + monkey.getMonkeyName() + "-" + monkey.getMonkeyAge() + "-" + monkey.getMonkeyGender()+"-"+monkey.getAnotherGender()); } tx.commit();
总结:Hibernate在初始化阶段,就会根据映射文件的映射信息为所有的持久化类预定义insert into table(a,b,c) values(?,?,?)/update/delete等语句;
?代表JDBC PreparedStatement中的参数,这些SQL语句都存放在SessionFactory缓存中,当执行Session的sava,delete,update等方法时,将从缓存中找到预定义的SQL语句,再把具体的参数绑定到该语句中执行,默认情况下,预定义的SQL语句包含了表的所有字段,不过我们可以控制:
<property name="monkeyName" update="false" column="mname" type="string" not-null="true" />
monkeyName映射了持久化类的属性,也对应表中的mname字段,update="false",代表我们在更新这条记录的时候不回去更新这个字段(默认更新所有字段);
一般情况下我们为了节省SQL的执行开销,会把dynamic-insert和dynamic-update都设置成true,这样SQL执行就只包含需要插入或更新的字段了;
SessionFactory代表一个数据库存储源,如果只有一个数据库存储源那么只需要创建一个SessionFactory实例,创建一个SessionFactory实例需要消耗很多资源,它是一个重量级的对象,通过创建一个SessionFactory获得session是一个轻量级的对象,每个session实例和一个事务绑定。
三、Hibernate的一些高级使用