1.配置文件 hibernate.cfg.xml
参见官方文档
2.映射文件 hbm.xml
参见官方文档
重点:主键生成方式
native:根据使用的数据库来确定id的生成方式
如果是插入操作的话,在插入之前就会对数据库进行一次访问来生成下一个id,然后才插入,也就是插入了之后才知道id
hilo:高低位方式,一部分是数据库生成的,另一部分是程序生成的,可以保证是不会重复的,这种方式和上面的不同之处在于它不是插入了之后才知道id的[我自己也很糊涂]
采用这种方式数据库中会生成一个表 hibernate-unique-key(默认情况),表中有一个字段 next-hi,意思就是下一个高位
如果是native类型获取主键,对mysql来说是自增长方式的,只有在插入数据库后才能拿到主键;而用hilo方式获取主键,这种方式不用马上插入数据也可以拿到主键
foreign:外键引用,通常要和one-to-one标签一起使用
在一对一映射中要指定引用的是哪个表的哪个字段
<id name="id">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
uuid:UUID的生成方式生成id时不用访问一次数据库,它生成的id肯定是不同的,并且是string
如果插入比较多的话,可以考虑这种方式,但是查询效率会降低,因为比较的是string
3.Hibernate的类型
(1)<property name=“name” type=“java.lang.String”/>
type可以是hibernate、java类型或者你自己的类型(需要实现hibernate的一个接口)。
(2)基本类型一般不需要在映射文件(hbm.xml)中说明,只有在一个JAVA类型和多个数据库数据类型相对应时并且你想要的和hibernate缺省映射不一致时,需要在映射文件中指明类型(如:java.util.Date,数据库DATE,TIME,DATATIME,TIMESTAMP,hibernate缺省会把java.util.Date映射成DATATIME型,而如果你想映射成TIME,则你必须在映射文件中指定类型)。
默认的情况下,如果 java.util.Date -> datetime(数据库中)
如果只想保存日期,type设置成date
如果只想保存时间,type设置成time
如果两个都想保存,type设置成timestamp
(3)自定义类型,只要实现了接口就行
文档:
数据类型的对应关系:
内置的 basic mapping types 可以大致地分类为:
integer, long, short, float, double, character, byte, boolean, yes_no, true_false
这些类型都对应 Java 的原始类型或者其封装类,来符合(特定厂商的)SQL 字段类型。boolean, yes_no 和 true_false 都是 Java 中 boolean 或者 java.lang.Boolean 的另外说法。
string
从 java.lang.String 到 VARCHAR(或者 Oracle 的 VARCHAR2)的映射。
date, time, timestamp
从 java.util.Date 和其子类到 SQL 类型 DATE,TIME 和 TIMESTAMP(或等价类型)的映射。
calendar, calendar_date
从 java.util.Calendar 到 SQL 类型 TIMESTAMP 和 DATE(或等价类型)的映射。
big_decimal, big_integer
从 java.math.BigDecimal 和 java.math.BigInteger 到 NUMERIC(或者 Oracle 的 NUMBER类型)的映射。
locale, timezone, currency
从 java.util.Locale,java.util.TimeZone 和 java.util.Currency 到 VARCHAR(或者 Oracle 的VARCHAR2 类型)的映射。Locale 和 Currency 的实例被映射为它们的 ISO 代码。TimeZone 的实例被影射为它的 ID。
class
从 java.lang.Class 到 VARCHAR(或者 Oracle 的 VARCHAR2 类型)的映射。Class 被映射为它的全限定名。
binary
把字节数组(byte arrays)映射为对应的 SQL 二进制类型。
text
把长 Java 字符串映射为 SQL 的 CLOB 或者 TEXT 类型。
serializable
把可序列化的 Java 类型映射到对应的 SQL 二进制类型。你也可以为一个并非默认为基本类型的可序列化 Java 类或者接口指定 Hibernate 类型 serializable。
clob, blob
JDBC 类 java.sql.Clob 和 java.sql.Blob的映射。某些程序可能不适合使用这个类型,因为blob 和 clob 对象可能在一个事务之外是无法重用的。(而且, 驱动程序对这种类型的支持充满着补丁和前后矛盾。)
imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date, imm_serializable, imm_binary
一般来说,映射类型被假定为是可变的 Java 类型,只有对不可变 Java 类型,Hibernate 会采取特定的优化措施,应用程序会把这些对象作为不可变对象处理。比如,你不应该对作为 imm_timestamp 映射的 Date 执行 Date.setTime()。要改变属性的值,并且保存这一改变,应用程序必须对这一属性重新设置一个新的(不一样的)对象。
实体及其集合的唯一标识可以是除了 binary、 blob 和 clob 之外的任何基础类型。
4.Session和SessionFactory
Session是非线程安全的,生命周期较短,代表一个和数据库的连接,在B/S系统中一般不会超过一个请求;
内部维护一级缓存和数据库连接,如果session长时间打开,会长时间占用内存和数据库连接。
SessionFactory是线程安全的,一个数据库对应一个SessionFactory,生命周期长,一般在整个系统生命周期内有效;
SessionFactory保存着和数据库连接的相关信息(user,password,url)和映射信息,以及Hibernate运行时要用到的一些信息。
线程安全 意思就是多个地方进行访问同一个数据时,数据是同步的,不会乱的,因为正在访问的那个数据会被锁死,其他人没有办法访问
还有一种线程安全是 static 的,静态的,不会改变的,以及只读的属性,它们的值是不能够被修改的(final)
5. flush时将一级缓存与数据库同步,这个方法Hibernate自己会在相应的时间调用,最好不要自己调用,因为这个方法耗时耗资源
一般在查询之前或者提交之前执行
测试代码:[注:Department是 native的id生成器,而Employee是hilo的形式]
/**
* @Author:胡家威
* @CreateTime:2011-8-15 下午11:18:42
* @Description:
*/
package com.yinger.main;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.yinger.domain.Department;
import com.yinger.domain.Employee;
import com.yinger.util.HibernateUtils;
public class One2Many {
public static void main(String[] args) {
add();
}
private static void add() {
Department depart = new Department();
depart.setName("depart name");
Employee emp1 = new Employee();
emp1.setName("emp1 name");
Employee emp2 = new Employee();
emp2.setName("emp2 name");
Set<Employee> emps = new HashSet<Employee>();
emps.add(emp1);
emps.add(emp2);
depart.setEmps(emps);
Session s = null;
Transaction tr = null;
try{
s = HibernateUtils.getSession();
tr = s.beginTransaction();
s.save(depart);
s.save(emp1);
s.save(emp2);
s.flush();
System.out.println("--------------------");
tr.commit();
}catch(Exception e){
if(tr!=null)
tr.rollback();
}finally{
if(s!=null)
s.close();
}
}
}
测试结果: 两条Employee的insert语句是在横线之上的
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set depart_id=? where id=?
Hibernate: update Employee set depart_id=? where id=?
--------------------
注释掉 s.flush() 方法之后,insert语句在横线的下面
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into Department (name) values (?)
--------------------
Hibernate: update Employee set depart_id=? where id=?
Hibernate: update Employee set depart_id=? where id=?
前者调用了flush方法,使得一级缓存和数据库进行了同步,这样就可以立即插入Employee对象了
后者则不会,Hibernate默认情况下都会对数据库操作进行优化,一般都会在最后的时刻进行数据库操作,也就是批量的进行数据库的CURD
这样可以提高数据库操作的效率
6.大批量处理
大量操作数据时可能造成内存溢出,解决办法如下:
①清除session中的数据
for(int i=0;i<100000;i++) session.save(obj);
for(int i=0;i<100000;i++){
session.save(obj);
if(i% 50 == 0){
session.flush(); // 一般要在clear之前进行一次flush操作,因为并不知道什么时候修改了数据,所以要在清除缓存之前和数据库进行一次同步
session.clear();
}
}
②用StatelessSession接口:它不和一级缓存、二级缓存交互,也不触发任何事件、监听器、拦截器,通过该接口的操作会立刻发送给数据库,与JDBC的功能一样。
StatelessSession s = sessionFactory.openStatelessSession();该接口的方法与Session类似。
③Query.executeUpdate()执行批量更新,会清除相关联的类二级缓存(sessionFactory.evict(class))
缺点:可能会造成级联和乐观锁定问题,因为二级缓存中的数据已被清空了
测试代码:
public static void main(String[] args) {
People p = addPeople();
System.out.println("---------");
excuteUpdateTest();
}
public static void excuteUpdateTest() {
// hibernate 3.0 之前,批量更新只能是把数据一个个取出来然后更新
Session session = HibernateUtils.getSession();
Transaction tx = session.beginTransaction();
query = session.createSQLQuery("select from people p").
List<People> list = query.list();
for(People people:list) {
people.getName().setFirstName("new firstName");
}
// hibernate 3.0 之后,有了 excuteUpdate 方法 update Customer set name = :newName
Query query2 = session.createQuery("update People set =:newName");
query2.setString("newName", "new name");
query2.executeUpdate();
tx.commit();
}
测试结果:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into People (version, first_name, last_name, id) values (?, ?, ?, ?)
---------
Hibernate: select p.id as id0_, p.version as version8_0_, p.first_name as first3_8_0_, p.last_name as last4_8_0_ from people p
Hibernate: update People set version=?, first_name=?, last_name=? where id=? and version=?
Hibernate: update People set first_name=?
数据库中的数据:
id version first_name last_name
1 1 new name lastName
7.拦截器和事件监听
拦截器与事件都是hibernate的扩展机制(类似过滤器),Interceptor接口是老的实现机制,现在改成事件监听机制;
他们都是hibernate的回调接口,hibernate在save,delete,update…等会回调这些类。
测试代码:Hibernate 3.0 之后将拦截器改成了事件监听!当然,拦截器还是有的
[ 这部分内容我测试失败,估计是我的Hibernate的版本的问题,实现的方法的返回值都是不同的,以下摘自百度]
以下摘自:http://hi.baidu.com/sonmeika/blog/item/b83d1c12aa687449f819b8b2.html 传智播客hibernate视频教程(十五):其他问题汇总
// 示例:
// 在hibernate.cfg.xml中添加:
<hibernate-configuration>
<session-factory name="sessionFactory">
......
<!-- 映射文件存放位置 -->
<mapping resource="cn/itcast/hibernate/domain/User.hbm.xml"/>
<mapping resource="cn/itcast/hibernate/domain/Department.hbm.xml"/>
<mapping resource="cn/itcast/hibernate/domain/Employee.hbm.xml"/>
<mapping resource="cn/itcast/hibernate/domain/Person.hbm.xml"/>
<mapping resource="cn/itcast/hibernate/domain/IdCard.hbm.xml"/>
<mapping resource="cn/itcast/hibernate/domain/Teacher.hbm.xml"/>
<mapping resource="cn/itcast/hibernate/domain/Student.hbm.xml"/>
<!-- 定义监听器 -->
< type="save">
<listener class="cn.itcast.hibernate.SaveListener"/>
</event>
</session-factory>
</hibernate-configuration>
SaveListener.java:
package cn.itcast.hibernate;
import org.hibernate.HibernateException;
import org.hibernate.event.SaveOrUpdateEvent;
import org.hibernate.event.SaveOrUpdateEventListener;
import cn.itcast.hibernate.domain.User;
public class implements {
private static final long serialVersionUID = -5774832715365370704L;
@Override
public void onSaveOrUpdate(SaveOrUpdateEvent event)
throws HibernateException {
if (event.getObject() instanceof cn.itcast.hibernate.domain.User) {
User user = (User) event.getObject();
System.out.println("--"+user.getId()); //如果有id值,就证明已经保存了
System.out.println("--"+user.getName());
}
}
}
这样在保存user时都会先经过onSaveOrUpdate。例如:
User user = new User();
user.setBirthday(new Date());
user.setName("name");
System.out.println("before save");
addUser(user);
System.out.println("after save");
输出如下:
before save
--0
--name
after save
在输出内容中看不到insert语句了,并且库里也没有添加记录。这是因为我们自定义的监听器覆盖了原有的监听器,hibernate认为已经定义了更好的监听器,就不执行默认的了。如果要继续执行默认的,则需要在hibernate.cfg.xml中增加如下:
<!-- 定义监听器 -->
<event type="save">
<listener class="cn.itcast.hibernate.SaveListener"/>
</event>
监听器的调用顺序与此处的顺序有关,先调用SaveListener,后调用DefaultSaveEventListener。
修改后的输出如下:
before save
--0 // ID是0,说明此时还没有保存
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into Persons (name, birthday, id) values (?, ?, ?)
after save
调整监听器的顺序如下:
<event type="save">
<listener class="org.hibernate.event.def.DefaultSaveEventListener"/><!-- 注意:是DefaultSaveEventListener,而不是DefaultSaveOrUpdateEventListener -->
<listener class="cn.itcast.hibernate.SaveListener"/>
</event>
输出如下:
before save
Hibernate: select hibernate_sequence.nextval from dual
--1 // ID是1,说明对象已保存
Hibernate: insert into Persons (name, birthday, id) values (?, ?, ?)
after save
8. SQL查询 和 命名查询
s.createSQLQuery:建议不要使用,因为兼容性不好,对于数据库的移植会造成影响
s.getNamedQuery:写在映射文件中的HQL,统一的管理,便于以后使用的方便
命名的sql放置在映射文件中,放置的位置不同(class内或者class外),传递的字符串的参数是不同的
测试代码:
在People的映射文件中添加:放在class外面
<query name="getFirstNameById">
<![CDATA[from People where id=:id]]>
</query>
测试类中的方法:
package com.yinger.main;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.yinger.domain.Name;
import com.yinger.domain.People;
import com.yinger.util.HibernateUtils;
public class QueryTest {
public static void main(String[] args) {
People p = addPeople();
System.out.println("---------");
sqlTest();
System.out.println("---------");
namedSqlTest(p.getId());
}
public static void sqlTest() {
Session session = HibernateUtils.getSession();
query = session.createSQLQuery("select {p.*} from people p").addEntity("p", People.class); //
List<People> list = query.list();
for(People people:list) {
System.out.println(people.getName().getFirstName());
}
}
public static void namedSqlTest(int id) {
Session session = HibernateUtils.getSession();
Query query = session.getNamedQuery("getFirstNameById");
query.setInteger("id", id);
List<People> list = query.list();
for(People people:list) {
System.out.println(people.getName().getFirstName());
}
}
public static People addPeople() {
People p = new People();
Name n = new Name();
n.setFirstName("firstName");
n.setLastName("lastName");
p.setName(n);
HibernateUtils.add(p);
return p;
}
private static void testTransaction(int id) {
Session s1 = HibernateUtils.getSession();
Transaction tx1 = s1.beginTransaction();//一个事务开启了
People p1 = (People)s1.get(People.class, id);
Session s2 = HibernateUtils.getSession();;
Transaction tx2 = s2.beginTransaction();//又开启了另一个事务,并且操作的是同一条数据
People p2 = (People)s2.get(People.class, id);
p2.getName().setFirstName("firstName 2");
p1.getName().setFirstName("firstName 1");
tx2.commit();
tx1.commit();
s1.close();
s2.close();
}
}
测试结果:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into People (version, first_name, last_name) values (?, ?, ?)
---------
Hibernate: select p.id as id0_, p.version as version8_0_, p.first_name as first3_8_0_, p.last_name as last4_8_0_ from people p
firstName
---------
Hibernate: select people0_.id as id, people0_.version as version8_, people0_.first_name as first3_8_, people0_.last_name as last4_8_ from People people0_ where (people0_.id=?)
firstName
9. 不合适使用 Hibernate的场景
①不适合OLAP(On-Line Analytical Processing 联机分析处理),以查询分析数据为主的系统;适合OLTP(on-line transaction processing 联机事务处理)。
联机分析处理 系统总是进行查询统计,没有很好的对象和关系模型,不适合使用 Hibernate
②对于些关系模型设计不合理的老系统,也不能发挥hibernate优势。
③数据量巨大,性能要求苛刻的系统,hibernate也很难达到要求, 批量操作数据的效率也不高。
10. 与 JPA 集成 (annotation方式)
①需要添加的包ejb3-persistence.jar, hibernate-entitymanager.jar, hibernate-annotations.jar, hibernate-commons-annotations.jar, jboss-archive-browsing.jar, javassist.jar
②配置文件%CLASSPATH%/META-INF/persistence.xml
③JAVA代码:
EntityManagerFactory emf = Persistence.createEntityManagerFactory(name);
//(Name:在persistence.xml中指定。)
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
Tx.begin();
Em.persist(entity);//remove,merge,find
Tx.commit();
Em.close();
Emf.close();
相关的PPT: