2010-10-22 73 views
3

我對Hibernate和PostgreSQL相當陌生,但到目前爲止它進展順利,儘管我現在遇到了一個我無法解決的問題。在第一次操作(這是插入或更新數據庫中的1000行的一個事務)填充數據庫時出現錯誤。錯誤是:Hibernate使用@Transactional創建了太多的連接,如何防止這種情況?

 
SQL Error: 0, SQLState: 53300 
FATAL: sorry, too many clients already 
Exception in thread "main" org.hibernate.exception.GenericJDBCException: Cannot open connection 

這是重要的代碼:

@Repository 
public class PDBFinderDAO extends GenericDAO<PDBEntry> implements IPDBFinderDAO { 
    @Override 
    @Transactional 
    public void updatePDBEntry(Set<PDBEntry> pdbEntrySet) { 
     for (PDBEntry pdbEntry : pdbEntrySet) { 
      getCurrentSession().saveOrUpdate(pdbEntry); 
     } 
    } 
} 

的getCurrentSession()從GenericDAO擴展並調用sessionFactory.getCurrentSession()。

這是我的Hibernate配置:

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 
<hibernate-configuration> 
    <session-factory> 
     <!-- Database connection settings --> 
     <property name="hibernate.connection.driver_class">org.postgresql.Driver</property> 
     <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property> 
     <property name="hibernate.connection.url">jdbc:postgresql://localhost/PDBeter</property> 
     <property name="hibernate.connection.username">xxxx</property> 
     <property name="hibernate.connection.password">xxxx</property> 

     <!-- Create or update the database schema on startup --> 
     <property name="hbm2ddl.auto">create</property> 

     <!-- Use the C3P0 connection pool provider --> 
     <property name="hibernate.c3p0.min_size">5</property> 
     <property name="hibernate.c3p0.max_size">20</property> 
     <property name="hibernate.c3p0.timeout">300</property> 
     <property name="hibernate.c3p0.max_statements">50</property> 
     <property name="hibernate.c3p0.idle_test_period">300</property> 

     <!-- Disable the second-level cache --> 
     <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> 

     <!-- Batch size --> 
     <property name="hibernate.jdbc.batch_size">50</property> 

     <!-- this makes sure the more efficient new id generators are being used, 
     though these are not backwards compatible with some older databases --> 
     <property name="hibernate.id.new_generator_mappings">true</property> 

     <!-- Echo all executed SQL to stdout --> 
     <!-- 
     <property name="hibernate.show_sql">true</property> 
     --> 
     <property name="format_sql">true</property> 
     <property name="use_sql_comments">true</property> 
    </session-factory> 
</hibernate-configuration> 

這是我的Spring配置:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:task="http://www.springframework.org/schema/task" 
    xsi:schemaLocation=" 
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
     http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> 


    <context:component-scan base-package="nl.ru.cmbi.pdbeter" /> 

    <!-- Transaction Manager --> 
    <bean id="transactionManager" 
     class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
     <property name="sessionFactory" ref="sessionFactory" /> 
    </bean> 

    <tx:annotation-driven /> 

    <!-- Session Factory --> 
    <bean id="sessionFactory" 
     class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 
     <property name="configLocation" value="hibernate.cfg.xml" /> 
     <property name="packagesToScan" value="nl.ru.cmbi.pdbeter.core.model.domain" /> 
    </bean> 

    <!-- Task Executor --> 
    <task:annotation-driven /> 
</beans> 

我真的不知道是怎麼回事錯了,這應該只打開每次一個連接,並在事後關閉它,是不是@Transactional應該做的事情?另外,是否有一種簡單的方法來檢查在特定時間打開了多少個連接,以便我可以在錯誤之前和之後檢查有多少個連接已打開?

編輯:當我檢查數據庫沒有添加任何東西,所以它甚至不能建立一個連接,這裏出了什麼問題?

編輯:我很抱歉,但我已經自己解決了。這是一個非常愚蠢的錯誤,有一個使用標準的非常小的查詢也被執行了1000次,但是那個在事務之前被執行,導致它在1000個單獨的事務/會話/連接中執行(我認爲是正確的我如果我錯了!)

編輯:好吧,事實證明,根本沒有解決它,因爲我需要那個小的查詢,看看是否已經在數據庫中,如果是這樣,獲取該對象從數據庫,所以我可以更新它的字段/列/無論你想調用它。

這是GenericDAO方法:

@Override 
public PDBEntry findByAccessionCode(String accessionCode) { 
    return (PDBEntry) createCriteria(Restrictions.eq("accessionCode", accessionCode)).uniqueResult(); 
} 

有是建立映射的對象是不是在DAO,因爲它轉換成原始數據文件到數據庫對象的功能,所以我想以防止數據庫操作出現並只將saveOrUpdate()放入數據庫模塊中。我現在遇到的問題是,在將原始數據文件轉換爲數據庫對象期間,findByAccessionCode()會被調用1000次,因爲我需要檢查數據庫中是否存在某個數據,如果是這樣,從數據庫中獲取對象而不是創建一個新對象。

現在如何在這種情況下在一個連接內執行該查詢1000次?我試着製作轉換方法,將1000個文件@transactional轉換,但這並不奏效。

這裏的轉換方法:

private void updatePDBSet(Set<RawPDBEntry> RawPDBEntrySet) { 
    Set<PDBEntry> pdbEntrySet = new LinkedHashSet<PDBEntry>(); 

    for (RawPDBEntry pdb : RawPDBEntrySet) { 
     PDBEntry pdbEntry = pdbEntryDAO.findByAccessionCode(pdb.id); 
     if (pdbEntry == null) { 
      pdbEntry = new PDBEntry(pdb.id, pdb.header.date); 
     } 

     pdbEntry.setHeader(pdb.header.header); 

     ExpMethod expMethod = new ExpMethod.Builder(pdbEntry, pdb.expMethod.expMethod.toString()).build(); 
     if (pdb.expMethod.resolution != null) { 
      expMethod.setResolution(pdb.expMethod.resolution); 
     } 
     if (pdb.expMethod.rFactor != null) { 
      expMethod.setRFactor(pdb.expMethod.rFactor.rFactor); 

      if (pdb.expMethod.rFactor.freeR != null) { 
       expMethod.setFreeR(pdb.expMethod.rFactor.freeR); 
      } 
     } 

     if (pdb.hetGroups != null) { 
      for (PFHetId hetId : pdb.hetGroups.hetIdList) { 
       HetGroup hetGroup = new HetGroup(pdbEntry, hetId.hetId); 

       if (hetId.nAtom != null) { 
        hetGroup.setNAtom(hetId.nAtom); 
       } 

       if (hetId.name != null) { 
        hetGroup.setName(hetId.name); 
       } 
      } 
     } 

     for (PFChain chain : pdb.chainList) { 
      new Chain(pdbEntry, chain.chain); 
     } 

     pdbEntrySet.add(pdbEntry); 
    } 

    pdbFinderDAO.updatePDBEntry(pdbEntrySet); 
} 

(該pdbFinderDAO。updatePDBEntry(pdbEntrySet)是我原本以爲問題起源的地方)

編輯:首先對不起,我創建了這個新帖子,我真的以爲我找到了答案,但我會繼續在這篇文章中進一步修改。

好吧,現在我把所有1000個findAccessionCode標準放在DAO裏面,把一組原始數據文件發送到DAO,這樣它就可以檢索到那裏的ID,然後在數據庫中找到它們,找到它可以找到的那些並將其添加到數據庫對象映射的HashMap中,並將原始數據文件的引用作爲關鍵字(因此我知道原始數據屬於哪個數據庫條目)。這個功能我做了@Transactional像這樣:

@Override 
@Transactional 
public Map<RawPDBEntry, PDBEntry> getRawPDBEntryToPDBEntryMap(Set<RawPDBEntry> rawPDBEntrySet) { 
    Map<RawPDBEntry, PDBEntry> RawPDBEntryToPDBEntryMap = new HashMap<RawPDBEntry, PDBEntry>(); 

    for (RawPDBEntry pdb : rawPDBEntrySet) { 
     RawPDBEntryToPDBEntryMap.put(pdb, (PDBEntry) createCriteria(Restrictions.eq("accessionCode", pdb.id)).uniqueResult()); 
    } 

    return RawPDBEntryToPDBEntryMap; 
} 

儘管如此,沒有成功...我得到確切同樣的錯誤,但它告訴我,這是導致它的標準。爲什麼我不能在同一個連接中執行所有這1000個查詢?

編輯:又一次更新:我試着添加所有的查詢1 1,這工作,慢慢地,但它的工作。我在一個空的數據庫上做了這個。接下來我想同樣的事情,但現在數據庫已經包含的東西,從第一次嘗試,我得到了以下錯誤:

 
Exception in thread "main" org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions 

我猜這事做的事實,我得到DAO中的對象(已經在數據庫中,因此必須更新),然後將引用發送迴轉換方法,然後更改它們的字段,然後將它們發送回DAO以使它們持久化。雖然google搜索了一下後,我發現人們有問題,他們的POJO與註釋集合:

@OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)

在級聯導致的問題。是否必須刪除所有這些級聯,並對所有不同映射對象的saveOrUpdate()操作進行硬編碼?或者這與這個錯誤沒有關係?

最後:我還沒有弄清楚如何一次爲1000個對象執行此操作。

+0

我認爲你應該發佈你最後的編輯作爲答案。 – 2010-10-25 08:57:50

+0

謝謝,好主意。這篇文章有點亂,我真的需要一個橡皮鴨...... – FinalArt2005 2010-10-25 09:12:22

回答

-1

解決了這個問題,它必須處理Spring的錯誤設置,導致@Transactional無法識別。我解決了這個問題,然後錯誤消失了。

+3

請問你能詳細說明一下那個配置不好的細節是什麼 – Tito 2015-02-20 16:30:52

相關問題