今天跟一个同事调试代码,突然发现,诸如findUserById(Integer uid)这样的方法,尽然产生了一条update的sql语句,感觉很奇怪,并没有发现任何更新的地方,怎么会产生这条update语句呢???
这个方法大致是这样的:
仔细检查代码,却发现,在User类中,有这样的做法:
以往用惯Hibernate的朋友(像我就是这样),可能会觉得find出一个对象,然后你怎么区更改这个对象,不会同步到数据库中去,而在JTA环境中,不是这样的,在JTA环境中,默认的,一个数据库的操作方法,在方法开始前就会开启一个事务,而在方法结束后就会提交这个事务,这个过程对程序员来说是透明的,程序员自己感觉不到,不用编写任何代码就能完成的事情。
不懂这个道理的朋友,可能很多人跟我一样,在学习的初期,大家都说EJB复杂,不好用,都被一本叫做XXX without EJB的书教唆至直接学习Spring + Hibernate + Status去了,一提起Spring都会说有多么多么的好,其中就有一个叫做“声明式事务管理”的东东,当时我一直不知道到底为啥要叫做“声明式事务”,就简单的理解成,用那个事务拦截器配置 add*、save*、delete*啊,这样配置,然后写出诸如addUser、saveUser这样的方法,就可以了,直到我有一次直去写jdbc的时候,我才知道,不使spring事务管理去写那个编程式事务到底是怎么回事:
于是第一次了解了到底声明式事务是咋回事,这一扯就扯远了,不过也很无奈,谁叫我们大学里之交了java的语法,然后人家公司一招聘就要ssh之类的,逼的大家都ssh去了,一向自强的我又不愿意去培训那个什么“北大青鸟”、“新东方”当时我上大学的时,记得是这两个培训机构闹的最火,学费好贵,不是1W就是2W,我当时有两个选择,一个是买太超酷的电脑,一个就是去那里培训,几经思考海是抵挡不住“迪兰恒景”显卡的诱惑,最终搞了台1.2W 的电脑,于是开始了一边打“孤岛惊魂”,一边学java的日子....扯太远了,还是回归正体.
在EJB环境下,有个叫做entityManager的东东,这个东西与hibernate的session不太一样,session里去出来的对象,你可以去任意的修改里边的属性,直到你自己调用update这样的方法,才会更新到数据库中,但是在EJB下,却不是那个样子。
EJB中的entityManager的持久华对象大致上有三种(还有多的,大家补充一下):
1.调用entityManager.persist(Object o).
2.调用entituManager.merge(Object o).
3.调用entityManager.find(Class clazz,Object o)
第一种:
原来在数据库中没有这条记录,调用后会同步到数据库,前提条件是事务提交,那么默认的EJB环境下,是会自动提交这个事务,如果说,你在执行:
这样最终,同步到数据库中的是user.name=22222.
为什么会这样呢,这与我们原来用的hibernate有很大的不同, 虽然hibernate也有persist这样的方法,但是很少在ssh这样的环境下使用,一般都只是使用save方法,那么
persist与save到底有什么不同呢,我怕自己描述不清除,从网上抄来了一段话:
persist把一个瞬态的实例持久化,但是并"不保证"标识符被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时间
用我的话说就是把一个对象交给entityManager来管理,但entiyManager并不保证立刻写进数据库,直到调用flush方法的时候才会提交,那么默认的当一个事务结束的时候,是会自动调用flash方法,这样才算吧对象同步到数据库中了,所以上边那个程序最终写到数据库中的是22222.
但是save方法会立即写入数据库.
第二种:
这个可以拿来与hibernate中的update方法来比较, 所谓要执行update,那表明了,数据库中已经有该对象所对应的记录,只是现在做一个更新操作,把对象中的属性值同步到数据库中去,当你执行update操作时,会立刻的打印出sql语句,而merge方法则不会,道理跟上边的save一样,只是把这个需要更新的对象交给entityManager管理,并不意味着立刻执行同步到数据库的操作,直到调用flush方法,entityManager才会去同步的数据库,同样的,事务结束的时候也会自动的调用flush方法.
第三种:
执行查找操作,如果数据库中有这条数据,则会返回一个引用给上层调用,那么这个过程经历了:
数据库-->entityManager-->findUserById,三个过程,这里我们吧findUserById这个方法叫做上层,它可以由更上层的方法调用,在调用findUserById方法时,默认的EJB容器会开启事务(如果findUserById还有更上层的方法也会同样的加入到这个事务中来),
拿我在最上边写的那个User类来说:
1.首先entityManager会从数据库中查找到记录
2.然后调用User的默认构造函数去生成一个对象
3.接着把数据库中的记录的值赋给这个新生对象,调用了User类的setXXX方法来赋值
4.把组装好的对象返回给上层调用
但上边代码setLastLoginTime的做法,意图是直接在bean里产生一个最新的时间,那么,在底层调用的时候,刚好使用了最新的Date实例覆盖了原有的值,导致了返回给上层的User对象的属性值与数据库中的不一致,所以执行一次findUserById导致产生update的操作.
不知不觉的开发过程貌似已经习惯了吧entity当作pojo来处理...
一点经验,写下来希望可以帮助大家:)
这个方法大致是这样的:
public User findUserById(Integer uid) {
return entityManager.find(User.class, uid);
}
仔细检查代码,却发现,在User类中,有这样的做法:
public class User implements Serializable {
... ...
private Date lastLoginTime;
public void setLastLoginTime(Date date) {
this.lastLoginTime = new Date();
}
... ...
}
以往用惯Hibernate的朋友(像我就是这样),可能会觉得find出一个对象,然后你怎么区更改这个对象,不会同步到数据库中去,而在JTA环境中,不是这样的,在JTA环境中,默认的,一个数据库的操作方法,在方法开始前就会开启一个事务,而在方法结束后就会提交这个事务,这个过程对程序员来说是透明的,程序员自己感觉不到,不用编写任何代码就能完成的事情。
不懂这个道理的朋友,可能很多人跟我一样,在学习的初期,大家都说EJB复杂,不好用,都被一本叫做XXX without EJB的书教唆至直接学习Spring + Hibernate + Status去了,一提起Spring都会说有多么多么的好,其中就有一个叫做“声明式事务管理”的东东,当时我一直不知道到底为啥要叫做“声明式事务”,就简单的理解成,用那个事务拦截器配置 add*、save*、delete*啊,这样配置,然后写出诸如addUser、saveUser这样的方法,就可以了,直到我有一次直去写jdbc的时候,我才知道,不使spring事务管理去写那个编程式事务到底是怎么回事:
tx.start();
... ...
tx.commit();
于是第一次了解了到底声明式事务是咋回事,这一扯就扯远了,不过也很无奈,谁叫我们大学里之交了java的语法,然后人家公司一招聘就要ssh之类的,逼的大家都ssh去了,一向自强的我又不愿意去培训那个什么“北大青鸟”、“新东方”当时我上大学的时,记得是这两个培训机构闹的最火,学费好贵,不是1W就是2W,我当时有两个选择,一个是买太超酷的电脑,一个就是去那里培训,几经思考海是抵挡不住“迪兰恒景”显卡的诱惑,最终搞了台1.2W 的电脑,于是开始了一边打“孤岛惊魂”,一边学java的日子....扯太远了,还是回归正体.
在EJB环境下,有个叫做entityManager的东东,这个东西与hibernate的session不太一样,session里去出来的对象,你可以去任意的修改里边的属性,直到你自己调用update这样的方法,才会更新到数据库中,但是在EJB下,却不是那个样子。
EJB中的entityManager的持久华对象大致上有三种(还有多的,大家补充一下):
1.调用entityManager.persist(Object o).
2.调用entituManager.merge(Object o).
3.调用entityManager.find(Class clazz,Object o)
第一种:
原来在数据库中没有这条记录,调用后会同步到数据库,前提条件是事务提交,那么默认的EJB环境下,是会自动提交这个事务,如果说,你在执行:
user.setName("111111");
entityManager.persist(user);
user.setName("222222");
这样最终,同步到数据库中的是user.name=22222.
为什么会这样呢,这与我们原来用的hibernate有很大的不同, 虽然hibernate也有persist这样的方法,但是很少在ssh这样的环境下使用,一般都只是使用save方法,那么
persist与save到底有什么不同呢,我怕自己描述不清除,从网上抄来了一段话:
persist把一个瞬态的实例持久化,但是并"不保证"标识符被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时间
用我的话说就是把一个对象交给entityManager来管理,但entiyManager并不保证立刻写进数据库,直到调用flush方法的时候才会提交,那么默认的当一个事务结束的时候,是会自动调用flash方法,这样才算吧对象同步到数据库中了,所以上边那个程序最终写到数据库中的是22222.
但是save方法会立即写入数据库.
第二种:
这个可以拿来与hibernate中的update方法来比较, 所谓要执行update,那表明了,数据库中已经有该对象所对应的记录,只是现在做一个更新操作,把对象中的属性值同步到数据库中去,当你执行update操作时,会立刻的打印出sql语句,而merge方法则不会,道理跟上边的save一样,只是把这个需要更新的对象交给entityManager管理,并不意味着立刻执行同步到数据库的操作,直到调用flush方法,entityManager才会去同步的数据库,同样的,事务结束的时候也会自动的调用flush方法.
第三种:
执行查找操作,如果数据库中有这条数据,则会返回一个引用给上层调用,那么这个过程经历了:
数据库-->entityManager-->findUserById,三个过程,这里我们吧findUserById这个方法叫做上层,它可以由更上层的方法调用,在调用findUserById方法时,默认的EJB容器会开启事务(如果findUserById还有更上层的方法也会同样的加入到这个事务中来),
拿我在最上边写的那个User类来说:
1.首先entityManager会从数据库中查找到记录
2.然后调用User的默认构造函数去生成一个对象
3.接着把数据库中的记录的值赋给这个新生对象,调用了User类的setXXX方法来赋值
4.把组装好的对象返回给上层调用
但上边代码setLastLoginTime的做法,意图是直接在bean里产生一个最新的时间,那么,在底层调用的时候,刚好使用了最新的Date实例覆盖了原有的值,导致了返回给上层的User对象的属性值与数据库中的不一致,所以执行一次findUserById导致产生update的操作.
不知不觉的开发过程貌似已经习惯了吧entity当作pojo来处理...
一点经验,写下来希望可以帮助大家:)