我對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個對象執行此操作。
我認爲你應該發佈你最後的編輯作爲答案。 – 2010-10-25 08:57:50
謝謝,好主意。這篇文章有點亂,我真的需要一個橡皮鴨...... – FinalArt2005 2010-10-25 09:12:22