Hibernate Session & Transcation 持久化对象的三种状态 + 实例
Reference : https://blog.youkuaiyun.com/mabixie/article/details/79440652
https://www.qingtingip.com/h_381958.html
https://blog.youkuaiyun.com/confirmAname/article/details/9324633
Hibernate的事务( Transaction对象 )通过Session的beginTransaction( )方法显式打开,Hibernate自身并不提供事务控制行为(没有添加任何附加锁定行为),Hibernate底层直接使用JDBC连接、JTA资源或其他资源的事务。
Hibernate只是对底层事务进行了抽象,让应用程序可以直接面向Hibernate事务编程,从而将应用程序和JDBC连接、JTA资源或其他事务资源隔离开了。从编程角度来看,Hibernate的事务由Session对象开启;从底层实现来看,Hibernate的事务由 TransactionFactory 的实例来产生。
TransactionFactory是一个事务工厂的接口,Hibernate为不同的事务环境提供了不同的实现类。如CMTTransactionFactory是针对容器管理事务环境的实现类、JDBCTransactionFactory是针对JDBC局部事务环境的实现类、JTATransactionFactory是针对JTA全局事务环境的实现类。
应用程序编程无须手动操作TransactionFactory产生事务,这是因为SessionFactory底层已经封装了TransactionFactory。
SessionFactory对象的创建代价很高,它是线程安全的对象,被设计成可以被所有线程所共享。通常SessionFactory会在应用程序启动时创建,一旦创建了SessionFactory将不会轻易关闭,只有当应用退出时才关闭SessionFactory。
Session对象是轻量级的,它是线程不安全的。对于单个业务进程,单个的工作单元而言,Session只被使用一次。创建Session时,并不会立即打开与数据库的连接,只有需要进行数据库操作时,Session才会获取JDBC连接。因此打开和关闭Session,并不会对性能造成很大的影响。甚至即使无法确定一个请求是否需要数据访问,也可以打开Session对象,因为如果不进行数据库访问,Session不会获取JDBC连接。
<Hibernate延迟加载机制
https://blog.youkuaiyun.com/qq_20864311/article/details/102688391>
使用hibernate进行增删改是要重新开启事务,使用查询时可以不用重新开启事务!
在Hibernate中持久化的对象可以划分为三种状态,分别是瞬时态、持久态和脱管态:
1、 瞬时态(transient)
瞬时态也称为临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID(相当于主键值),尚未与Hibernate Session关联,在数据库中也没有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中是孤立存在的,与数据库中的数据无任何关联,仅是一个信息携带的载体。
2、 持久态(persistent)
持久态的对象存在持久化标识OID ,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。
3、 脱管态(detached)
脱管态也称离线态或者游离态,当某个持久化状态的实例与Session的关联被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。
【how to assign OID?
持久化类要有一个唯一标识OID与表的主键对应。因为Hibernate中需要通过这个唯一标识OID区分在内存中是否是同一个持久化类。在Java中通过地址区分是否是同一个对象的,在关系型数据库的表中是通过主键区分是否同一条记录。那么Hibernate就是通过这个OID来进行区分的。Hibernate是不允许在内存中出现两个OID相同的持久化对象的。
【持久化类的编写规则 :
- 持久化类需要提供无参数的构造方法。因为在Hibernate的底层需要使用反射生成类的实例。
- 持久化类的属性需要私有,对私有的属性提供公有的get和set方法。因为在Hibernate底层会将查询到的数据进行封装。
- 持久化类的属性要尽量使用包装类的类型。因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更清晰而基本数据类型不容易描述。
- 持久化类尽量不要使用final进行修饰。因为Hibernate中有延迟加载的机制,这个机制中会产生代理对象,Hibernate产生代理对象使用的是字节码的增强技术完成的,其实就是产生了当前类的一个子类对象实现的。如果使用了final修饰持久化类。那么就不能产生子类,从而就不会产生代理对象,那么Hibernate的延迟加载策略(是一种优化手段)就会失效。.】】
Session和线程绑定
在Hibernate中,可以通过代码来操作管理事务,如通过“Transaction tx = session.beginTransaction();”开启一个事务;持久化操作后,通过“tx.commit();”提交事务;如果事务出现异常,又通过“tx.rollback();”操作来撤销事务(事务回滚)。
除了在代码中对事务开启,提交和回滚操作外,还可以在Hibernate的配置文件中对事务进行配置。配置文件中,可以设置事务的隔离级别。其具体的配置方法是在hibernate.cfg.xml文件中的<session-factory>标签元素中进行的。
Reference : https://bbs.youkuaiyun.com/topics/200045119
hibernate要求一个session一个事务吗?
public class UserOperate {
/**This Class is the tool class for Class User
* @param null
*/
private Session session = null;
public UserOperate() {
File file = new File("hibernate.cfg.xml");
if (file.exists()) {
Configuration config = new Configuration().configure(file);
SessionFactory factory = config.buildSessionFactory();
this.session = factory.openSession();
} else {
System.out.println("error");
System.exit(0);
}
}//构造函数,应该不会错吧
public void insert(User user) {
session.getTransaction().begin();
this.session.save(user);
session.getTransaction().commit();
//session.close(); 我也不知道什么时候关闭session
}//插入User数据函数
@SuppressWarnings("unchecked")
public User queryByName(String name){
User user=null;
String hql="FROM User as p where p.name=?";
Query q=this.session.createQuery(hql);
q.setString(0,name);
List<User> userList=q.list();
Iterator<User> iter=userList.iterator();
if(iter.hasNext()){
user=iter.next();
}
return user;
}//按名字查找数据
public void delete(String name){
String hql="delete User where name=?";
Query q=this.session.createQuery(hql);
q.setString(0, name);
session.getTransaction().begin();
q.executeUpdate();
session.getTransaction().commit();
session.close();
}//删除数据
@SuppressWarnings("unchecked")
public List<User> queryAll(){
List<User> list=null;
String hql="from User";
Query q=this.session.createQuery(hql);
list=q.list();
return list;
}//查找所有
@SuppressWarnings("unchecked")
public List<User> queryNameLike(String content){
List<User> list=null;
String hql="from User where name like ?";
Query q=this.session.createQuery(hql);
q.setString(0, "%"+content+"%");
list=q.list();
return list;
}//模糊查询
public boolean canLogin(String name,String password){
boolean flag=false;
User userGet=this.queryByName(name);
if(userGet!=null){
password=Md5Encrypt.encrypt(Md5Encrypt.encrypt(password));
if(userGet.getPassword().equals(password))
flag=true;
else
System.out.println("密码错误");
}
else{
System.out.println("没有该用户存在");
}
return flag;
}//自己写的判断能否登录的函数,其中两次对password加密,可以判断提取出来的密码跟输入的password的一致性
}
我的入口类,经过很多次试验,每对数据库进行一次操作就要new 一个UserOperate对象出来,否则整个程序只能做最后一次分配的操作,帮忙看看:
public class Object {
public static void main(String[] args) {
User user=new User();
UserOperate userop=new UserOperate();
user.setName("Newflypig");
user.setPassword("8215085");
userop.insert(user);
for(int i=0;i<10;i++){
user.setName("dingding"+String.valueOf(i));
user.setPassword("newflypig");
new UserOperate().insert(user);
}
List<User> list=new UserOperate().queryAll();
Iterator<User> iter=list.iterator();
while(iter.hasNext()){
System.out.println(iter.next().getName());
}
System.out.println(new UserOperate().canLogin("dingding0", "newfypig"));
}
}
解答:
在你的程序里User user=new User();
此时的user的状态为瞬时状态,即user还未与session进行关联。
你new UserOperate对象同时即声明了一个session。
当你调用UserOperateUser.insert(user)时,即将此user对象同当前的session进行关联此时的user对象即为持久状态,因此在你循环中不管对此user操作多少次仅是对关联后的user进行修改。
当你在循环中 new UserOperate().insert(user)时你所使用的user对象所关联的是不同session。
因此你成功的插入了10条数据。
如果你使用循环的话建议你对原有程序进行修改
Transaction.begin();
for(int i=0;i<10;i++){
User user=new User();
user.setName("dingding"+String.valueOf(i));
user.setPassword("newflypig");
session.save(user);
}
Transaction.commit();
Hibernate建议采用每个请求对应一次Session的模式------一次请求通常表示需要执行一个完整的业务功能,这个功能由系列的数据库原子操作组成,而且它们应该是一个逻辑上的整体。
每个请求对应一个Session的模式,不仅可以用于设计操作单元,甚至,很多业务处理流程都需要组合一系列的用户操作,即用户对数据库的交叉访问。
在某些应用架构中,特别是对于那些需要使用Hibernate进行数据访问的代码,以及那些需要在不同应用层和不同进程中使用Hibernate的应用,如何保证Session处于打开状态也是一个问题。通常有两种方法可以解决此问题:
① 在一个web应用中,可以利用过滤器Filter,在用户请求结束、页面生成结束时关闭Session。也就是保证在视图显示层一直打开Session,这就是所谓的Open Session in View模式。当然采用这种模式时,必须保证所有异常得到正确处理,在呈现视图界面之前,或在生成视图界面的过程中发生异常时,必须保证可以正确关闭Session,并结束事务。
② 使用业务逻辑层来负责准备数据,在业务逻辑层返回数据之前,业务逻辑层对每个所需集合调用Hibernate.initialize()方法,或者使用带fetch子句或FetchMode.JOIN的查询,事先取得所有数据,并将这些数据封装成VO(值对象)集合,然后程序可以关闭Session了。业务逻辑层将VO集传入视图层,让视图层只负责简单的显示逻辑。在这种模式下,可以让视图层和Hibernate API 彻底分离,保证视图层不会出现持久层API,从而提供更好的解耦。