SSH 学习笔记
Hibernater Hql
Hibernater分页查询失败,查询结果为所有,原因:
package com.tenni.test; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.tenni.entity.Account; import com.tenni.entity.Service; import com.tenni.util.HibernateUtil; public class testHql { private Session session = HibernateUtil.getSession(); private Transaction ts = session.getTransaction(); @Test //分页查询 public void testPaging(){ int page=1, pageSize = 3,totalRows,from; String hql = "select count(*) from Service"; Query qry = session.createQuery(hql); totalRows =Integer.valueOf(qry.uniqueResult().toString()); System.err.println("所有的 数量 totalRows==="+totalRows); hql = "from Service order by id"; qry = session.createQuery(hql);//必须在设置起始和容量之前 from = (page-1)*pageSize; qry.setFirstResult(from);//设置起点,从0开始 qry.setMaxResults(pageSize);// 设置页容量 List<Service> list = qry.list(); for(Service a:list){ System.out.println(a.getId()+" "+a.getOsUserName()+" "+a.getLoginPassword()); } } }
Hibernate 二级缓存管理异常情况及原因
再用ThreadLocal线程方式管理session的时候,HibernateUtil中静态获取session的方法以次线程为key,放入ThreadLocal的map中。(自己的理解不知道对否)
- 如果想要close掉session必须用HibernateUtil.close();否则提示异常:Session is closed!(如下代码)
- HibernateUtil.close()关闭session,必须在HibernateUtil.clearSessionFactory(e1<你从session中get的实体对象>) 清理二级缓存实体类之前。否则执行清理二级缓存之后,Hibernate依然不执行查询语句,从二级缓存中获取共享的查询实例对象结果。
附:HibernateUtil.getSessionFactory().getAllClassMetadata() 可以查看sessionFactory中的一些实体类信息
测试代码如下:
@Test // 二级缓存是SessionFactory级缓存,由它负责管理,因此需要获取到SessionFactory才能管理二级缓存, // 我们先在HibernateUtil中增加获取SessionFactory的方法 public void test2() { Session session1 = HibernateUtil.getSession(); Emp e1 = (Emp) session1.get(Emp.class, 1708); System.out.println(e1.getEname()); System.out.println("-----------session1-----"+session1); // session1.close(); HibernateUtil.close();//前 HibernateUtil.clearSessionFactory(e1);//后 System.out.println("-----------session1-----"+session1); Session session2 = HibernateUtil.getSession(); Emp e2 = (Emp) session2.get(Emp.class, 1708); System.out.println("-----------session2-----"+session2); System.out.println(e2.getEname()); }
控制台输出:session1已经成功关闭
-----------session1-----SessionImpl(PersistenceContext[entityKeys=[EntityKey[com.tenni.entity.Emp#1708]],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[]]) -----------要close()====连接--SessionImpl(PersistenceContext[entityKeys=[EntityKey[com.tenni.entity.Emp#1708]],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[]]) -----------清理二级缓存中----实体类查询缓存 -----------session1-----SessionImpl(<closed>)
HibernateUtil.java
package com.tenni.util; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static SessionFactory sessionFactory; /* 使用ThreadLocal管理Session,可以保证一个线程中只有唯一的一个连接。 * * 并且我们在获取连接时,它会自动的给我们返回当前线程对应的连接。 */ private static ThreadLocal<Session> tl = new ThreadLocal<Session>(); static{ // 加载Hibernate配置文件 Configuration conf = new Configuration(); conf.configure("/hibernate.cfg.xml"); sessionFactory = conf.buildSessionFactory(); } /** * 创建session */ public static Session getSession(){ // ThreadLocal会以当前线程名为key获取连接 Session session = tl.get(); // 如果取到的当前的连接为空 if(session == null){ // 使用工厂方法创建连接 System.out.println("-----------getSession===使用工厂方法创建连接"); session = sessionFactory.openSession(); // ThreadLocal会以当前线程名为key保存session tl.set(session); } System.out.println("-----------getSession===使用tl.get() 获取连接--"+session); return session; } /** * 清理二级缓存中清理掉 实体类查询缓存 */ public static void clearSessionFactory(Object o){ try { sessionFactory.evict(o.getClass()); System.out.println("-----------清理二级缓存中----实体类查询缓存"); } catch (Exception e) { System.out.println("xxxxxxxxxxx清理二级缓存中 实体类查询缓存 !!失败!!----"+e); } } /** * @return the sessionFactory */ public static SessionFactory getSessionFactory() { return sessionFactory; } /** *关闭session */ public static void close(){ // ThreadLocal会以当前线程名为key获取session Session session = tl.get(); if(session != null){ System.out.println("-----------要close()====连接--"+session); session.close(); // 将当前线程对应的连接从ThreadLocal移除 tl.remove(); } } public static void main(String[] args) { System.out.println("-----------"+getSession()); System.out.println("-----------"+tl); close(); } }
Session is closed!
org.hibernate.SessionException: Session is closed! at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:49) at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:874) at org.hibernate.impl.SessionImpl.get(SessionImpl.java:815) at org.hibernate.impl.SessionImpl.get(SessionImpl.java:808) at com.tenni.test.TestSecondCache.test2(TestSecondCache.java:48) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59) at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79) at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87) at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77) at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42) at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88) at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51) at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44) at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27) at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37) at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Hibernate 的动态update语句,实现部分字段更新
使用Hibernate修改时,它会根据映射关系文件,自动拼出一个update语句,然后执行修改,其中映射关系文件中往往配置表中全部的 字段,而很多时候,页面上不需要修改表中全部的字段,只需要修改其中的一部分,因此页面上的字段少于表中字段,在提交保存时,缺少的字段就成了空值,那么 再按照完整的update语句来执行更新,就会把这些不需要更新的字段更新为空。
这个问题如果使用JDBC或者MyBatis是不会存在的,因为SQL是自己写的。而Hibernate自动生成SQL,就出现了这个问题。
本案例中,我们采用动态更新的方式来解决这个问题,即在映射关系文件中通过dynamic-update=”true”来声明更新方式为动态 更新,届时Hibernate在自动生成update语句时会判断属性值是否发生改变,若改变则将属性拼入SQL,否则忽略掉这个属性。
这种方式要求传给Hibernate的对象必须是持久态的,而通过页面传入的对象是Struts2自动初始化的,是临时态的。我们可以通过 ID查询出持久态对象,然后通过Spring中的BeanUtils工具类,将临时态对象的属性值复制给持久态对象,然后用这个持久态对象进行更新即可。
<class name="com.tarena.entity.Cost" table="cost" dynamic update="true"> <id name="id" type="integer" column="id"> <!-- 用来指明主键的生成方式 --> <generator class="sequence"> <param name="sequence">cost_seq</param> </generator> </id> .........
Struts2 重定向同一个namespace的action可以简写
如:重定向至、/cost/findByPage
<action name="saveCost" class="costAction“ method="saveCost"> <result name="success" type="redirectAction"> <param name="namespace">/cost</param> <param name="actionName">findByPage</param> </result> </action> 改为: <action name="saveCost" class="costAction" method="saveCost"> <result name="success" type="redirectAction"> findByPage </result> </action>