web经验

来源:互联网 发布:有软件卸载不了 编辑:程序博客网 时间:2024/06/08 06:06

1.hibernate一对多或多对多映射中,<set>的lazy属性要使用默认的true。否则在数据量较大的时候肯定会出现内存不足的异常的。

Session关闭之后将不能使用懒加载,可以使用如下hql语句查询需要的对象:

select m from Department d join d.members m where d.id=xxx

其中Department是部门实体,它有由成员members构成的set属性。


2.SSH框架开发的时候,action的生命周期设置为“request”,使用带状态的action是一个很不明智的选择。


3.hibernate一对多双向关联的一般配置

    ①many-to-one标签

    lazy属性:一般设置为false,只关联一个对象可以直接加载,避免使用的时候再次访问数据库,也不会造成内存的大量消耗。

    cascade属性:一般设置为none。级联操作通常会导致多余的update语句执行,如果业务没有特别需求尽量设置为none。

    ②set标签

    inverse:一般设置为true。inverse用于设置当前类对持有的对象或集合是否维护关联关系。inverse设置为true表示控制权翻转,当前类不负责维护关联关系。

    cascade:一般设置为delete。逻辑业务上通常被引用的一方(父级元素)被删除之后,引用该记录的记录也应该被删除。在是层级较多的时候,使用级联删除可以替代编程方式的递归删除。不过现在越来越多的业务使用假删除,也就是将要删除的对象的状态设置为删除(通常还会有正常、冻结等多种其他状态),以便在必要的时候恢复被删除的数据。相关观点参见本文第9条。

    lazy:采用默认值true。


4.使用hibernate的update方法更新数据库中的一条记录时,只要被更新对象的主键属性正确设置,数据库中的记录就能被正确更新,而不必在意当前被更新的对象是瞬时状态、游离状态还是持久化状态。此外使用update方法更新数据库记录时,必须一次性更新实体的所有属性对应的数据库中的列。如果在jsp页面的表单当中,我们不想更新的列没有对应的表单项,这些列的值会被更新为null,而不是我们想象的保持原样。所以如果只想更新表中的部分列,需要采取额外的措施处理。一般来收,要么在修改属性的jsp页面设置隐藏的表单项放置这些列所对应的实体的属性,要么在后台将不需要改变的列的值赋值给update方法的实体参数。


5.spring整合hibernate4.x

    Hibernate4.0之后的版本,spring中不再提供持久化层HibernateDaoSupport作为基类。网上的说法是,spring团队认为hibernate自身的方法和事务管理已经足够好,开发人员在持久化层获得hiber的session并用它进行各种持久化操作就足够了。

    具体做法是在spring的配置文件中定义id为sessionFactiory的bean,在程序的dao层的实现类中注入sessionFactory,调用sessionFactory的getCurrentSession方法获得session进而执行各种持久化操作。

private SessionFactory sessionFactory;public void setSessionFactory(SessionFactory sessionFactory) {this.sessionFactory = sessionFactory;}public long update(String hql, Object...params) {Session session = sessionFactory.getCurrentSession();Query query = session.createQuery(hql);for (int i = 0; i < params.length; i++) {query.setParameter(i, params[i]);}return query.executeUpdate();}
     说明:在上面的例子中使用了动态数组作为参数,它的好处是可以传递任意数量的参数,即使一个参数都没有也不会有问题,不用担心params.length会报错,java会准确的处理这些情况。

    sessionFactory的getCurrentSession方法获得的session必须处于事务管理之中。程序运行中调用dao层的方法报“当前线程中没有session”的错误时,只要在service层调用dao层接口的方法上添加事务就可以了。


6.应用hibernate延迟加载的问题

    hibernate各种标签中的lazy属性默认值是true,延迟加载的对象只有在被使用的时候才会被载入。但是如果延迟加载的部分的使用,是在主体部分被加载的事务之外发生,就会产生“could not initialize proxy - no Session”(Hibernate4中的异常信息)的错误。

    解决办法:一是在加载主体部分的事务中,调用Hibernate.initialize(Object proxy)方法,把需要在事务之外调用的对象加载进来;二是在HQL语句中使用迫切左外连接,这样会覆盖掉配置文件中的延迟加载,使用立即加载策略。不过迫切左外连接的查询结果可能会重复,需要用HashSet处理一下;三是在需要的时候使用hql语句查询获得对象,而不使用延迟加载。

    说明:网上的另一种解决方案是配置OpenSessionInView,基本原理是使用一个过滤器保持在一次请求中的session始终处于打开状态,Spring也提供对这种解决方式的支持。应该说在绝大多数的项目中这样做是没有问题的,但是极端情况下还是不推荐这种方案。首先这样会导致session生命周期不必要的延长,从而引起非必要的资源占用;其次session是非线程安全的,应该避免多个事务共用同一个session。所以对于高并发高安全性的项目还是回归本质,用我上面提到的比较原始的方法解决吧!


7.Mysql表中插入新纪录

    一般情况下我们会设置mysql表的自增主键,所以插入新纪录的时候主键是无需赋值的。但是如果主键不赋值,我们就必须在insert语句的表名之后把其他列名都列举出来,然后再values之后一一对应的为各个列赋值。如果列比较多的话,一一写出各个列名是很麻烦的。解决方法是在insert语句中表名之后不写列名,values关键词之后,主键列赋值null,其他列依次赋值。也就是用下文的②取代①:

insert into t_user(uname,pwd) values('James','James');#①insert into t_user values(null,'James','James');#②

8.Hibernate通过java实体类和配置文件生成表的问题

    Hibernate通过实体类和hbm.xml配置文件生成表失败的原因可能是表名使用了数据库的关键字,解决方法是在hbm.xml的配置文件中表名用引用符号(键盘上与~位于同一按键的符号)括起来。


9.要不要配置hibernate的双向一对多关联

    以订单Order和客户Customer为例,一个订单只能属于一个客户,而一个客户可以有多个订单,典型的多对一。

    那么多对一关系该配置成单向的呢,还是双向的呢?

    配置成双向的一个好处就是可以通过一的一方方便的获得多的一方的实体。尽管如此,hibernate默认对多的一方是延迟加载的,这也是正确的思路。这样会造成session关闭之后无法延迟加载,于是这一好处也不明显了。而且通过一的一方获得多的一方很不灵活,无法在获得的时候对多的一方进行筛选、排序,从而分页也很不方便,只能获得所有关联实体之后再在程序中完成筛选、排序。

    双向多对一的另一个好处,也是比较实际的一个好处就是由一的一方级联删除多的一方。而且这一方便性在多对一自关联情况下体现的尤为明显。例如每一个部门可以有多个子部门,同时每一个部门可以有一个或者没有父部门。要删除一个父级单位,理论上要把其所有的子单位删除。如果没有配置一对多映射,就要采用编程的方式递归调用删除方法来层层删除子单位。而配置了双向关联并把set的cascade属性设置为all(或者delete),只需要load或get得到实体,然后delete就可以删除单位自身及子单位了。

    只配置单向的多对一的话,在需要的时候通过hql语句查询多的一方,可以更精细的对多的一方进行控制。

from Order o where o.customer.id=? and 其他筛选条件 order by 排序方式;

    一般来说,建议只配置单向关联。如果需要,配置双向关联,但是只在删除的时候使用一对多关联,算是一种折中的做法吧!注意为保证性能,后一种做法要把多对一关联的cascade设置为none,一对多关联的cascade的设置为delete。

    关于效率:经实际验证,级联保存10000个对象的时间是3000毫秒级别;在一个session中新建10000个对象,然后循环保存的时间是2800毫秒级别;在Dao层写一个保存单个对象的方法,在service层循环调用保存10000个对象的时间是2700毫秒级别;在Dao层写保存对象方法,在action层投过service层的方法循环调用保存10000个对象的时间是6400毫秒级别。而后两者的差距在于action层调用service层的方法,每次都要开启新的事务。而级联保存和自己在同一事务中写方法保存对象的效率是没有差别的。

    说明:上述关于效率的验证使用hibernate的getCurrentSession获得session,该方法返回的session与线程绑定,事务开始即产生session,事务结束或回滚即关闭session,所以在dao层调用session和在service层调用dao层的方法效率是一样的,前提是调用过程始终处于同一事务之中。