首先奉上
Hibernate3.2 API地址:http://docs.jboss.org/hiberna...
Hibernate4.3 API地址:http://docs.jboss.org/hiberna...
Hibernate 4.3文档:http://hibernate.org/orm/docu...html
情景0:请设置OpenSessionInViewFilterjava
情景1:在service外使用了dao.getSession()从而致使问题。mysql
解决思路:
其实个人Service层是加了@Transactional注解的,可是因为某些缘由,在service外使用了dao.getSession()从而致使该问题。
问题代码以下:(这段代码没有放在被@Transactional注解的Serivce层,从而致使问题)spring
Criteria c=storeDao.getSession().createCriteria(Store.class).add(Restrictions.or(Restrictions.isNull("mainImgJson"),Restrictions.isNull("introImgJson"))); c.createAlias("terrace", "terrace").add(Restrictions.eq("terrace.keyId", "1")); c.addOrder(Order.desc("updateTime")); Page<Store> page=storeManager.findPage(c, 1, 100);
解决方法就是在这段代码调用方法上加上@Transactional注解。
参考:http://stackoverflow.com/ques...
固然此处更好的办法是采用DetachedCriteriasql
情景2:Service[抽象]父类数据库方法没有加@Transactional
假设有如下类:数据库
@Transactional public class SubClass extends SuperClass { public void loadDb(){ //数据库操做 } } public class SuperClass { public void savedb() { //数据库操做 } }
savedb是父类的方法,loadDb是子类的方法。若是有如下调用:api
@Test public void test(){ SubClass o = new SubClass(); o.savedb();//将会报没有Session的错误 o.loadDb();//正常 }
解决方法:在父类中标注@Transactional(父类是抽象类也能够
):数组
@Transactional public class SuperClass { public void savedb() { } }
参考:http://www.cnblogs.com/xiefei...缓存
情景3:其余场景终极解决方案:session
有多是在非请求线程(请求线程有openssessioninviewfilter把控)某些查询延迟加载致使的.
对于这种情形,
第一:你能够设置lazy=false,不过不少状况下不现实;
第二:对于须要后续的数据,查询出来以后当即get(),或者size(),这样就能触发查询。还可使用Hibernate.initialize方法
在使用hibernate进行持久化时,有时须要动态的改变对象的加载,好比在编辑页面里面lazy=true,而在浏览页面lazy=false,这样能够在须要lazy的地方才进行控制。而配置文件中Lazy属性是全局控制的,如何处理呢?
当元素或者元素的lazy属性为true时,load() or get() or
find()加载这些对象时,Hibernate不会立刻产生任何select语句,只是产生一个Obj代理类实例,只有在session没有关闭的状况下运行Obj.getXxx()时才会执行select语句从数据库加载对象,若是没有运行任何Obj.getXxx()方法,而session已经关闭,Obj已成游离状态,此时再运行Obj.getXxx()方法,Hibernate就会抛出"Could
not initialize proxy - the owning Session was
closeed"的异常,是说Obj代理类实例没法被初始化。然而想在Session关闭以前不调用Obj.getXxx()方法而关闭Session以后又要用,此时只要在Session关闭以前调用Hibernate.initialize(Obj)或者Hibernate.initialize(Obj.getXxx())便可,net.sf.hibernate.Hibernate类的initialize()静态方法用于在Session范围内显示初始化代理类实例。
在配置文件里面能够用lazy=true,在程序里面能够用强制加载的方法Hibernate.initialize(Object
proxy) 方法强制加载这样就至关于动态改变为lazy=false。
但在使用时须要注意的一点是:其中的proxy是持久对象的关联对象属性,好比A实体,你要把A的关联实体B也检出,则要写Hibernate.initialize(a.b)。
Hibernate 4.2以后,你还能够配置hibernate.enable_lazy_load_no_trans:
hibernate.xml:
<property name="hibernate.enable_lazy_load_no_trans">true</property>
Sinche Hibernate 4.2 you can use
.setProperty("hibernate.enable_lazy_load_no_trans", "true"); this
option solves the dreaded org.hibernate.LazyInitializationException:
could not initialize proxy - no Session which took so many hours of
life to programmers away.
具体能够看https://docs.jboss.org/hibern...
对于使用HQL的童鞋,能够显式join fetch
Query query = session.createQuery( "from Model m " + "join fetch m.modelType " + "where modelGroup.id = :modelGroupId" );
第三:查询时动态设置当即join,好比
crit.setFetchMode("pays", FetchMode.JOIN); crit.setFetchMode("ville", FetchMode.JOIN); crit.setFetchMode("distination", FetchMode.JOIN);
第三部分总结:
0、在场景为Request请求线程时,配置Open Session in View 具体原理,请看https://vladmihalcea.com/2016...
一、使用HQL时能够join fetch (推荐);或者你也能够用uniqueResult()方法,该方法会当即查出关联对象。
二、使用criteria时能够FetchMode.JOIN
三、显式调用Hibernate.initialize(obj),Hibernate.initialize(obj.getXXX())
四、配置hibernate.enable_lazy_load_no_trans:(使用时请注意开销)
hibernate.xml: <property name="hibernate.enable_lazy_load_no_trans">true</property> persistence.xml: <property name="hibernate.enable_lazy_load_no_trans" value="true"/>
解释请参考:https://vladmihalcea.com/2016...
可是,使用hibernate.enable_lazy_load_no_trans配置时,你须要注意下面这一点:
Behind the scenes, a temporary Session is opened just for initializing
every post association. Every temporary Session implies acquiring a
new database connection, as well as a new database transaction.The more association being loaded lazily, the more additional
connections are going to be requested which puts pressure on the
underlying connection pool. Each association being loaded in a new
transaction, the transaction log is forced to flush after each
association initialization
翻译一下就是:这种情形下,每次初始化一个实体的关联就会建立一个临时的session来加载,每一个临时的session都会获取一个临时的数据库链接,开启一个新的事物。这就致使对底层链接池压力很大,并且事物日志也会被每次flush.
设想一下:假如咱们查询了一个分页list每次查出1000条,这个实体有三个lazy关联对象,那么,恭喜你,你至少须要建立3000个临时session+connection+transaction.
五、使用 DTO projection (推荐):解释请参考https://vladmihalcea.com/2016...
其实就是定义一个单独的DTO来保存查询结果。
public class PostCommentDTO { private final Long id; private final String review; private final String title; public PostCommentDTO( Long id, String review, String title) { this.id = id; this.review = review; this.title = title; } public Long getId() { return id; } public String getReview() { return review; } public String getTitle() { return title; } }
List<PostCommentDTO> comments = doInJPA(entityManager -> { return entityManager.createQuery( "select new " + " com.vladmihalcea.book.hpjp.hibernate.fetching.PostCommentDTO(" + " pc.id, pc.review, p.title" + " ) " + "from PostComment pc " + "join pc.post p " + "where pc.review = :review", PostCommentDTO.class) .setParameter("review", review) .getResultList(); }); for(PostCommentDTO comment : comments) { LOGGER.info("The post title is '{}'", comment.getTitle()); }
参考:http://stackoverflow.com/ques...
第四:若是因为某些要求,你不能按以上操做进行:那么请看终极解决方案:
/** * 因为在http请求线程以外,openssessioninviewfilter不起做用,致使线程没有绑定session,这个类就是为了解决此问题。 * 本身管理Hibernate Session的Runnable。 * @author taojw */ public abstract class TxSessionRunnable implements Runnable { @Override public final void run(){ SessionFactory sessionFactory = (SessionFactory)SpringContextHolder.getApplicationContext().getBean("sessionFactory"); boolean participate = bindHibernateSessionToThread(sessionFactory); try{ execute(); }finally{ closeHibernateSessionFromThread(participate, sessionFactory); } } public void execute(){ } public static boolean bindHibernateSessionToThread(SessionFactory sessionFactory) { if (TransactionSynchronizationManager.hasResource(sessionFactory)) { // Do not modify the Session: just set the participate flag. return true; } else { Session session = sessionFactory.openSession(); session.setFlushMode(FlushMode.MANUAL); SessionHolder sessionHolder = new SessionHolder(session); TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder); } return false; } public static void closeHibernateSessionFromThread(boolean participate, Object sessionFactory) { if (!participate) { SessionHolder sessionHolder = (SessionHolder)TransactionSynchronizationManager.unbindResource(sessionFactory); SessionFactoryUtils.closeSession(sessionHolder.getSession()); } } }
参考
http://stackoverflow.com/ques...
http://blog.csdn.net/zhengwei...
http://stackoverflow.com/ques...
能够不能够先获取一个延迟加载的对象,不当即调用Hibernate.initialize,而关闭session,而后在须要的时候,打开另一个session,调用Hibernate.initialize来初始化延迟的对象呢?
答案是否认的。不能够!
参考:https://stackoverflow.com/que...
Entity objects are pretty much "bound" to original session where they were fetched and queries like this cannot be performed in this way. One solution is to bound entity to new session calling session.update(entity) and than this new session knows about entity. Basically new query is issued to populate entity fields again.
So avoid this if not necessary and try to fetch all needed data in original session
或者你也能够经过获取主键去再次主动查询关联对象
产生此问题的缘由:
有两张表,table1和table2.产生此问题的缘由就是table1里作了关联<one-to-one>或者<many-to-one unique="true">(特殊的多对一映射,实际就是一对一)来关联table2.当hibernate查找的时候,table2里的数据没有与table1相匹配的,这样就会报No row with the given identifier exists这个错.
注解配置解决方法:
使用hibernate 注解配置实体类的关联关系,在many-to-one,one-to-one关联中,一边引用自另外一边的属性,若是属性值为某某的数据在数据库不存在了,hibernate默认会抛出异常。解决此问题,加上以下注解就能够了:@NotFound(action=NotFoundAction.IGNORE)
,意思是找不到引用的外键数据时忽略,NotFound默认是exception
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ICT_BASE_ID", referencedColumnName = "ID", unique = false, nullable = false, insertable = false, updatable = false) @NotFound(action=NotFoundAction.IGNORE) public IctBase getIctBase() { return ictBase; }
参考:http://blog.csdn.net/h3960710...
http://www.cnblogs.com/rixian...
Hibernate4.3以后,咱们能够直接使用JPA注解:"@CreationTimestamp" and "@UpdateTimestamp"
protected Date insertTime; protected Date updateTime; @Temporal(TemporalType.TIMESTAMP) @Column(updatable = false) @CreationTimestamp public Date getInsertTime() { return insertTime; } public void setInsertTime(Date insertTime) { this.insertTime = insertTime; } @UpdateTimestamp @Temporal(TemporalType.TIMESTAMP) public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
若是使用以前版本,那么你能够给dao封装一个save方法,并规定inserttime,updatetime字段名。统一处理便可。
参考:http://stackoverflow.com/ques...
http://stackoverflow.com/ques...
hibernate的速度性能并不差,固然了这和应用的数据库有关,在Oracle上,hibernate支持 hibernate.jdbc.fetch_size和 hibernate.jdbc.batch_size,而MySQL却不支持,而我原来的项目绝大多数都是使用MySQL的,因此以为速度慢,其实在企业级应用,尤为是金融系统大型应用上,使用Oracle比较多,相对来讲,hibernate会提高系统不少性能的。
hibernate.jdbc.fetch_size 50 //读
hibernate.jdbc.batch_size 30 //写
hiberante.cfg.xml(Oracle ,sql server 支持,mysql不支持)
<property name="hibernate.jdbc.fetch_size">50</property> <property name="hibernate.jdbc.batch_size">30</property>
这两个选项很是很是很是重要!!!将严重影响Hibernate的CRUD性能!
Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。
例如一次查询1万条记录,对于Oracle的JDBC驱动来讲,是不会1次性把1万条取出来的,而只会取出Fetch Size条数,当纪录集遍历完了这些记录之后,再去数据库取Fetch Size条数据。
所以大大节省了无谓的内存消耗。固然Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。 但内存消耗正好相反 建议使用Oracle的必定要将Fetch Size设到50
。
不过并非全部的数据库都支持Fetch Size特性,例如MySQL就不支持
。
Batch Size是设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,有点至关于设置Buffer缓冲区大小的意思。
Batch Size越大,批量操做的向数据库发送sql的次数越少,速度就越快。!
参考:http://blog.csdn.net/rick_123...
好比
@Table( name = "product_serial_group_mask", uniqueConstraints = {@UniqueConstraint(columnNames = {"mask", "group"})} )
And
@Column(unique = true) @ManyToOne(optional = false, fetch = FetchType.EAGER) private ProductSerialMask mask; @Column(unique = true) @ManyToOne(optional = false, fetch = FetchType.EAGER) private Group group;
区别在哪里?
前者是联合约束、后者分别是独立约束。
参考:http://stackoverflow.com/ques...
好比多对多时,Book有个Set<Author>类型属性,Author有个Set<Book>的属性。其实你不用管authors是Set类型。查询照样写就行,以下:
select a.firstName, a.lastName from Book b join b.authors a where b.id = :id
参考:http://stackoverflow.com/ques...
http://www.cnblogs.com/kingxi...
错误的查询语句:
String sql = "select a.* from tb_doc_catalog a where a.cat_code like '"+catCode+"%'"; Session session = this.getSession(); try { List catNameList = session.createSQLQuery(sql).list(); return catNameList ; } finally { releaseSession(session); //释放session }
分析:原来是查询出来的字段并不能自动转换为bean对象。
解决思路一(采用hql查询):
String sql = "select a from DocCatalogInfo a where a.catCode like '"+catCode+"%'"; List catNameList =getHibernateTemplate().find(sql); return catNameList ;
ok,测试一下没问题。
解决思路二(采用原生sql查询):
String sql = "select a.* from tb_doc_catalog a where a.cat_code like '"+catCode+"%'"; Session session = this.getSession(); try { List catNameList = session.createSQLQuery(sql).addEntity(DocCatalogInfo.class).list(); return catNameList ; } finally { releaseSession(session); //释放session }
ok。
hibernate 中createQuery与createSQLQuery二者区别是:
前者用的hql语句进行查询,后者能够用sql语句查询
前者以hibernate生成的Bean为对象装入list返回 ,后者则是以对象数组进行存储
其实,createSQLQuery有一个addEntity
方法能够直接转换对象
Query query = session.createSQLQuery(sql).addEntity(XXXXXXX.class);
对于链接了多个表的查询,可能在多个表中出现一样名字的字段。下面的方法就能够避免字段名重复的问题:
List cats = sess.createSQLQuery( " select {cat.*} from cats cat " ).addEntity( " cat " , Cat. class ).list();
addEntity()方法将SQL表的别名和实体类联系起来,而且肯定查询结果集的形态。
addJoin()方法能够被用于载入其余的实体和集合的关联.
List cats = sess.createSQLQuery( " select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id " ) .addEntity( " cat " , Cat. class ) .addJoin( " kitten " , " cat.kittens " ) .list();
原生的SQL查询可能返回一个简单的标量值或者一个标量和实体的结合体。
Double max = (Double) sess.createSQLQuery( " select max(cat.weight) as maxWeight from cats cat " ) .addScalar( " maxWeight " , Hibernate.DOUBLE); .uniqueResult();
命名SQL查询
@NamedQuery("persons")
List people = sess.getNamedQuery( "persons" ).setString( " namePattern " , namePattern) .setMaxResults( 50 ) .list();
返回一个Map对象,代码以下
Query query = session.createSQLQuery("select id,name from Tree t where pid in (select id from Tree) ").setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); //返回一个map,KEY:为DB中名称一致(大小写一致)遍历list时就能够 Map map = (Map)list.get[i];
map.get("id");map.get("name");来取值。按你的SQL语句select后的字段名来做为map的Key,但这个key必须与数据库中的字段名如出一辙。
参考:http://blog.csdn.net/stonejon...
Query query1 = session.createSQLQuery("select count(*) from user u where u.name like ? ").setParameter(0, "%lis%") ; Query query2 = session.createQuery("select count(*) from User ") ; Sysstem.out.println((Long) query1.list().get(0)) ; //wrong Sysstem.out.println((Long) query2.list().get(0)) ; //right
缘由:原生的SQL语句中count()返回值为BigInteger,而HQL语句中的count()返回值位Long型的,网上说的主要是兼容JPA而作的。
当使用createSQLQuery时,其返回值为BigInteger类型;
当使用createQuery时,其返回值位Long型的。
解决:
public long getMusicCount(long id){ String sql="select count(1) from music_singer_music where mid=?"; Object obj=dao.getSession().createSQLQuery(sql).setParameter(0, id).uniqueResult(); if(obj instanceof Long){ return (Long)obj; }else if(obj instanceof BigInteger){ return ((BigInteger)obj).longValue(); } return 0; }
参考:http://blog.csdn.net/u0137625...
清空缓存
当调用session.evict(customer); 或者session.clear(); 或者session.close()方法时,Session的缓存被清空。
清理缓存
Session具备一个缓存,位于缓存中的对象处于持久化状态,它和数据库中的相关记录对应,Session可以在某些时间点,按照缓存中持久化对象的属性变化来同步更新数据库,这一过程被称为清理缓存。
在默认状况下,Session会在下面的时间点清理缓存。
当应用程序调用org.hibernate.Transaction的commit()方法的时候,commit()方法先清理缓存,而后在向数据库提交事务;
当应用程序调用Session的list()或者iterate()时(【注】get()和load()方法不行),若是缓存中持久化对象的属性发生了变化,就会先清理缓存
,以保证查询结果能能反映持久化对象的最新状态;
当应用程序显式调用Session的flush()方法的时候。
上面第二点解释了为何在list()查询是有个时候会出现update语句。
http://blog.csdn.net/xwz0528/...
1 getCurrentSession建立的session会和绑定到当前线程,而openSession每次建立新的session。
2 getCurrentSession建立的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭
在一个应用程序中,若是DAO 层使用Spring 的hibernate 模板,经过Spring 来控制session 的生命周期,则首选getCurrentSession()。
在 SessionFactory 启动的时候, Hibernate 会根据配置建立相应的 CurrentSessionContext ,在 getCurrentSession() 被调用的时候,实际被执行的方法是 CurrentSessionContext.currentSession() 。
Hibernate4以后与Spring结合需配置为
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
在 getCurrentSession() 被调用的时候,实际被执行的方法是 SpringSessionContext.currentSession()
让我设想如下情景:
Session s1=sessionFactory.openSession();
s1.beginTransaction();
Object a=s1.get(Item.class,new Long(123));
Object b=s1.get(Item.class,new Long(123));
// a==b为true,a、b指向相同的内存地址。
s1.getTransaction.commit();
s1.close();
Session s2=sessionFactory.openSession();
s2.beginTransaction();
Object c=s2.get(Item.class,new Long(123));
//a==c返回false,a,c指向不一样的内存地址
s2.getTransaction.commit();
s2.close();
/*如今a,b,c都处于脱管状态/
Set set=new HashSet();
set.add(a);
set.add(b);
set.add(c);
/*输出什么呢?1?,2?,3?/
System.out.println(set.size())
咱们知道set是经过调用集合元素的equals方法来确保元素惟一性的。而Object的equals方法默认就是经过obj1==obj2来经过内存地址判断同一性。
那咱们如今知道了把。上述会输出2。可是咱们知道这两个元素都是表明数据裤中的同一行。因此这个时候咱们就须要覆盖equals与hashCode方法了。建议咱们对全部的实体类都覆盖这两个方法,由于咱们没法保证这种状况不会发生。
覆盖实体类的equals方法能够选择两种方式:
一、经过判断业务键而非数据库主键的相等性。若是业务键是惟一的话,推荐此方式。
二、经过判断对象的全部属性的相等性。