2010-10-22 2 views
3

Je suis assez nouveau pour Hibernate et PostgreSQL, mais jusqu'ici tout va bien, même si je suis confronté à un problème que je ne peux pas résoudre. J'obtiens une erreur lors du remplissage de la base de données lors de la toute première opération (une transaction insérant ou mettant à jour 1000 lignes dans la base de données). L'erreur est:Hibernate crée trop de connexions en utilisant @Transactional, comment éviter cela?

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

Ce code est importante:

@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() est prolongée de GenericDAO et appelle sessionFactory.getCurrentSession().

C'est ma configuration 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> 

Voici ma 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> 

Je ne suis pas vraiment sûr de ce qui va mal, cela ne devrait ouvrir une connexion à chaque fois, et le fermer après, n'est-ce pas ce que @Transactional est supposé faire? De plus, y a-t-il un moyen facile de vérifier combien de connexions sont ouvertes à un moment donné afin que je puisse vérifier avant et après l'erreur combien de connexions étaient ouvertes?

EDIT: quand je vérifie la base de données rien n'a été ajouté, donc il ne peut même pas faire une connexion, qu'est-ce qui ne va pas ici?

EDIT: Je suis désolé mais je l'ai déjà résolu moi-même. C'était une erreur très stupide, il y avait une très petite requête utilisant des critères qui a été exécutée 1000 fois aussi, mais celle-là a été exécutée avant la transaction, l'exécutant dans 1000 transactions/sessions/connexions séparées (je pense, correct moi si je me trompe!)

EDIT: ok, il s'avère que cela ne l'a pas résolu du tout, parce que j'avais besoin de cette petite requête pour voir si quelque chose était déjà dans la base de données, et si oui, obtenir cet objet de la base de données afin que je puisse mettre à jour ses champs/colonnes/tout ce que vous voulez l'appeler.

Ceci est la méthode dans le GenericDAO:

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

Il y a une fonction qui construit les objets mis en correspondance qui ne sont pas dans le DAO, car il convertit un fichier de données brutes dans l'objet de base de données, donc je voulais pour garder cela hors des opérations de base de données et ne placez le saveOrUpdate() dans le module de base de données. Le problème que j'ai maintenant est que le findByAccessionCode() est appelé 1000 fois pendant la conversion du fichier de données brut en objets de base de données, parce que je dois vérifier si un certain morceau de données est déjà présent dans la base de données, et si , récupère l'objet de la base de données au lieu d'en créer un nouveau.

Maintenant comment j'exécuterais cette requête 1000 fois dans une connexion dans ce contexte? J'ai essayé de faire cette méthode de conversion qui convertit les 1000 fichiers @transactional, mais cela n'a pas fonctionné.

est ici la méthode de conversion:

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); 
} 

(Le pdbFinderDAO.updatePDBEntry (pdbEntrySet) était où j'ai initialement pensé que le problème est apparu)

EDIT: Tout d'abord désolé que j'ai créé ce nouveau post, je pensais vraiment avoir trouvé la réponse, mais je vais continuer dans ce post pour d'autres modifications . Ok, maintenant je mets tous les 1000 critères findAccessionCode dans le DAO en envoyant un ensemble de fichiers de données brutes au DAO afin qu'il puisse y récupérer l'identifiant, puis les trouver dans la base de données, obtenant ceux qu'il peut trouver et l'ajouter à un HashMap où l'objet de base de données est mappé avec la référence au fichier de données brutes comme clé (donc je sais quelles données brutes appartiennent à quelle entrée de base de données). Cette fonction fait je @Transactional comme ceci:

@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; 
} 

encore, sans succès ... je reçois exactement la même erreur, mais il ne me dit ce sont les critères qui sont les causes. Pourquoi ne puis-je pas exécuter toutes ces requêtes 1000 dans la même connexion?

EDIT: Encore une autre mise à jour: J'ai essayé d'ajouter toutes les requêtes 1 par 1, et cela a fonctionné, lentement, mais cela a fonctionné. Je l'ai fait sur une base de données vide. Ensuite, j'ai essayé la même chose, mais maintenant la base de données contient déjà des les choses du premier coup, et je suis l'erreur suivante:

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

Je devine que cela a quelque chose à voir avec le fait que je reçois les objets (qui sont déjà dans la base de données et doivent donc être mis à jour) dans DAO, puis en renvoyant des références à la méthode de conversion, puis en changeant leurs champs, puis en les renvoyant au DAO pour les rendre persistants. Bien après googler un peu j'ai trouvé des gens qui avaient des problèmes avec des collections dans leur POJO avec de l'annotation:

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

où la cascade a causé le problème. Dois-je supprimer toutes ces cascades et coder en dur les opérations saveOrUpdate() pour tous les objets mappés différents? Ou cela n'a rien à voir avec cette erreur?

Et enfin: je ne suis toujours pas près de comprendre comment faire cela pour 1000 objets à la fois.

+0

Je pense que vous devriez publier votre dernière modification comme réponse. –

+0

Merci, bonne idée. Ce message est un peu un gâchis, j'ai vraiment besoin d'un canard en caoutchouc ... – FinalArt2005

Répondre

-1

Résolu le problème, il a dû faire avec une mauvaise configuration de Spring, ce qui a causé le @Transactional à ne pas être reconnu. J'ai corrigé cela, et ensuite l'erreur est partie.

+3

pouvez-vous s'il vous plaît spécifier les détails quelle était cette mauvaise configuration du printemps – Tito

Questions connexes