Hibernate 简易入门教程
Hibernate
HIbernate主要包含如下几个接口:Session,Query,Criteria以及Transaction。这些接口的实现在幕后是紧密相连的。
在一个HIbernate应用程序中,通过实质上改变他们的状态来保存和加载对象,我们在工作单元中进行这项工作。单个工作单元是一组被认为是原子团的操作。
1、开始工作单元
Session session1 = factory.openSession(); Transaction tx1 = session1.beginTransaction();
一个新的持久化上下文被初始化了并同时开始了一个事务,它将管理这个session中所有的对象。如果需要访问多个数据库就会有多个sessionfactory对象
sessionfactory对象的创建十分昂贵,所以不应该针对同一个数据库连接重复创建sessionfactory对象。
2、持久化对象
Message item = new Message(); Serializable itemId = session1.save(item); tx1.commit(); session1.close();
调用save()使得瞬时对象item变成了持久化,同时调用commit()时对持久化对象所做的变化与数据库进行了同步,hibernate获得了一个jdbc连接,并执行sql insert语句。save()函数将返回持久化实例的数据库标识符(取决于自己配置的标识符生成器)。
session最终被close()方法关闭,item引用处于脱管状态。
如果在事务发生期间发生异常了,hibernate对持久化对象所做的所有变化将进行数据库级别的回滚。要注意的是,hibernate将不会把内存变化会滚到持久化对象。因此需要放弃失败的session。
3、获取持久化对象
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Object item = session.get(Message.class, new Long(1234)); tx.commit(); session.close();
hibernate提供了get、load两种方法通过标识符获取持久化对象。获取到的对象处于持久化状态,一旦持久化上下文关闭,该对象将处于脱管状态。
get和load的一个区别在于当无法找到给定标识符关联的对象时,get()方法返回一个null而load()方法将抛出ObjectNotFoundException。
更重要的是load()方法始终试图返回一个代理,即不发生数据库命中而get()方法从不返回代理,每一次都将命中数据库。
4、修改持久化对象
对持久化实例进行修改,当事务的commit()被调用时,所做的修改将被传播到数据库。这种机制叫做自动脏检查(意味着hibernate追踪并保存在持久化状态中对一个对象所作出的改变)。
5、使持久化对象变成瞬时对象
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Message item = new Message(); item.setText("msg1"); Serializable itemId = session.save(item); Object obj = session.load(Message.class, (Long)itemId); session.delete(obj); tx.commit(); session.close();
调用delete()之后,这个对象处于移除状态(removed),此时不应该再继续使用它。commit()后,执行SQL DELETE操作。Session关闭后,该对象被认为是一个普通的瞬时实例。最终将被垃圾回收器收回。
如果启用了hibernate.use_identifier_rollback配置选项,hibernate会在删除后设置被删除的对象的标识符属性为NULL,成为一个可以在后期被复用的干净瞬时实例。
6、复制对象
当需要从一个数据库获取对象并把它们保存在另一个数据库中的时候。这称做对象的复制。
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Message item = new Message(); Serializable itemId = session.save(item); Message theItem = (Message) session.get(Message.class, itemId); tx.commit(); session.close(); Session session2 = sessionFactory2.openSession(); Transaction tx2 = session2.beginTransaction(); session2.replicate(item, ReplicationMode.LATEST_VERSION); tx2.commit(); session2.close();
ReplicationMode参数说明:
ReplicationMode.IGNORE:当现有的数据库行包含与目标数据库中相同的标识符时忽略对象。
ReplicationMode.OVERWRITE:覆盖任何包含与目标数据库中相同标识符的现有数据库行。
ReplicationMode.EXCEPTION:如果现有的数据库行包含与目标数据库中相同的标识符时抛出异常。
ReplicationMode.LASTEST_VERSION:如果目标数据库的版本对对象早则覆盖,否则忽略。
使用脱管对象
1、重附被修改的脱管实例
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Message item = new Message(); Serializable itemId = session.save(item); Message theItem = (Message) session.get(Message.class, itemId); tx.commit(); session.close(); theItem.setText("hello"); Session session2 = sessionFactory.openSession(); Transaction tx2 = session2.beginTransaction(); session2.update(theItem); tx2.commit(); session2.close();
这里有一个需要注意的地方是 当设置select-before-update="true"时,hibernate在update前将进行select检查是否有属性发生了变化(即是否为脏对象) 否则将不执行update操作
2、使托管对象变成瞬时
Session session2 = sessionFactory.openSession(); Transaction tx2 = session2.beginTransaction(); session2.delete(theItem); tx2.commit(); session2.close();
调用delete函数内部进行了两步操作,首先将对象重附到session,再对该对象进行删除操作。托管对象的重附是在几个session中间传输数据的唯一可行方法。
3、合并脱管对象的状态
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Message item = new Message(); Serializable itemId = session.save(item); Message theItem = (Message) session.get(Message.class, itemId); tx.commit(); session.close(); Session session2 = sessionFactory.openSession(); Transaction tx2 = session2.beginTransaction(); Message item2 = (Message) session2.get(Message.class, itemId); session2.update(item); tx2.commit(); session2.close();
尝试运行如上代码将会抛出异常:A different object with the same identifier value was already associated with the session。Hibernate无法确定哪个对象表示当前状态。
Session session2 = sessionFactory.openSession(); Transaction tx2 = session2.beginTransaction(); Message item2 = (Message) session2.get(Message.class, itemId); Message item3 = (Message) session2.merge(item); System.out.println(item == item2); //false System.out.println(item == item3); //false System.out.println(item2 == item3); //true session2.update(item);
tx2.commit();
session2.close();
调用merge()函数内部进行了如下几个动作:
1、HIbernate检查当前持久化上下文中的持久化实例是否具有与正在合并的脱管实例相同的数据库标识符。
2、如果有找到相同标识符的持久化实例,HIbernate把脱管实例的状态复制到持久化实例中去。
3、如果持久化上下文中没有相等的持久化实例,HIbernate就从数据库中加载他,然后将脱管状态对象及被获取的对象进行合并。
4、如果在数据库中也没有找到相等标识符的实例,就会创建新的持久化实例并将被合并的实例复制到新的持久化实例上,最后插入到数据库中并通过merge返回。
5、如果传递到merge函数中的是一个瞬时实例,而不是脱管对象,也会发生插入。
JPA
1、持久化实体实例
EntityManagerFactory emf = Persistence.createEntityManagerFactory("helloworld"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Message item = new Message(); item.setText("XXXX"); em.persist(item); tx.commit(); em.close();
注意:persist不会返回实体实例的数据库标识符。Session也实现了persist方法 但是Session不会把persist()操作级联到被关联的实体和集合,只是被级联到调用persist()可以到达的实体。
2、获取实体实例
EntityManager em2 = emf.createEntityManager(); EntityTransaction tx2 = em2.getTransaction(); tx2.begin(); Message finded = em2.find(Message.class, new Long(1)); Message finded2 = em2.getReference(Message.class, new Long(1111));
tx2.commit(); em2.close();
find函数始终命中数据库,如果找不到包含指定标识符的持久化实例,find就返回null。
getRerence函数始终不会命中数据库, 且当没有找到指定标识符实例时将抛出异常
3、修改持久化的实体实例
Java Persistence在事务提交后执行与数据库的同步.等同于HIbernate session提供的自动脏检查
4、使持久化实体实例变成瞬时
Java Persistence的remove方法语义和HIbernate Session中的delete()方法相同。但是要注意的是,不能对脱管状态的实体进行remove操作 否则会抛出异常。必须先合并脱管实例,再移除被合并的对象。
5、清除持久化上下文
对持久化实体实例所做的所有修改都在某个时间点与数据库同步, 这个过程称做清除。
每当EntityTransaction中的Commit被调用时,就清除EntityManager的持久化上下文。但是JPA实现被允许在其他的时间点上与持久化上下文同步
HIbernate作为一个JPA同步,在下列时间点同步:
EntityTransaction被提交时
执行查询时
当应用程序显式地调用flush()时