web-dev-qa-db-fra.com

Java/Hibernate - Les opérations d'écriture ne sont pas autorisées en mode lecture seule.

J'ai eu une exception ennuyeuse beaucoup ces derniers temps, et après quelques recherches sur Google et ce forum, je n'ai toujours pas trouvé de réponse qui pourrait résoudre mon problème.

Voici la chose - parfois, j'obtiens l'erreur suivante en essayant de mettre à jour ou de créer un nouvel objet avec Hibernate:

org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
at org.springframework.orm.hibernate3.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.Java:1186)
at org.springframework.orm.hibernate3.HibernateTemplate$12.doInHibernate(HibernateTemplate.Java:696)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.Java:419)
at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.Java:374)
at org.springframework.orm.hibernate3.HibernateTemplate.save(HibernateTemplate.Java:694)

Ce qui est vraiment étrange, c'est que, parfois, lors de la mise à jour d'un objet avec la méthode getHibernateTemplate().saveOrUpdate(object);, cela fonctionnera, mais parfois avec le même objet et en appelant la même méthode, cela ne fonctionne pas, mais cela semble dépendre de la manière dont j'obtiens l'objet la première place.

Exemple: disons que j'ai une table avec 3 champs: id, type, longueur. Ce qui peut arriver, c’est que si j’obtiens l’objet par l’identifiant et mets à jour la longueur, cela fonctionnera. Si je l'obtiens par le type et mets à jour la longueur, alors cela ne fonctionnera pas. Donc, ce que j'ai fait jusqu'ici pour éviter le problème, c'est de chercher dans l'objet la méthode qui ne posera pas de problème plus tard, mais cela devient de plus en plus ennuyeux d'essayer de trouver un moyen qui fonctionne.

De plus, j'ai maintenant cette exception lorsque j'essaie de créer un objet (mais pas tous, mais uniquement sur une table spécifique) et je ne trouve pas le moyen de contourner le problème. Et j’ai essayé d’ajouter @Transactional(readOnly = false) dans la transaction, mais cela n’a rien changé, et afficher le mode disait que je n’étais de toute façon pas en lecture seule.

Aucune suggestion?

Edit du 26 juillet: Voici quelques configurations liées à la veille prolongée

<property name="hibernateProperties">
    <props>
        <prop key="jdbc.fetch_size">20</prop>
        <prop key="jdbc.batch_size">25</prop>
        <prop key="cglib.use_reflection_optimizer">true</prop>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
        <prop key="connection.autoReconnect">true</prop>
        <prop key="connection.autoReconnectForPools">true</prop>
        <prop key="connection.is-connection-validation-required">true</prop>
    </props>
</property>

aussi, si cela peut aider

<property name="transactionAttributes">
    <props>
        <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="execute*">PROPAGATION_REQUIRED</prop>
        <prop key="add*">PROPAGATION_REQUIRED</prop>
        <prop key="create*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="delete*">PROPAGATION_REQUIRED</prop>
    </props>
</property>

Éditer le 31 août: Le code approprié dans ma classe qui étend HibernateDaoSupport, pour enregistrer les objets est:

public void createObject(Object persisObj) {
    getHibernateTemplate().save(persisObj);
}
14
Guillaume

J'ai changé la propriété de session unique du filtre de vue. Problème résolu:

  <filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    <init-param>
      <param-name>singleSession</param-name>
      <param-value>false</param-value>
    </init-param>
  </filter>
6
Fırat KÜÇÜK

Ce message d'erreur apparaît généralement lorsque vous utilisez Spring OpenSessionInViewFilter et que vous tentez d'effectuer des opérations de persistance en dehors d'une transaction gérée par Spring. Le filtre définit la session sur FlushMode.NEVER/MANUAL (selon les versions de Spring et Hibernate que vous utilisez - elles sont à peu près équivalentes). Lorsque le mécanisme de transaction Spring commence une transaction, il change le mode de vidage en "COMMIT". Une fois la transaction terminée, il est remis à NEVER/MANUAL, selon le cas. Si vous êtes absolument sûr que cela ne se produit pas, le coupable le plus probable est l'utilisation non sécurisée des threads d'une session. La session Hibernate ne doit être utilisée que dans un seul thread. Si cela passe entre les fils, toutes sortes de chaos peuvent se produire. Notez qu'une entité chargée à partir de Hibernate peut contenir une référence à la session dans laquelle elle a été chargée, et que le transfert de l'entité entre les threads peut donc également donner accès à la session depuis un autre thread.

11
Ryan Stewart

ajouter

@Transactional

au-dessus de votre fonction

7
AhmadReza

Je suis juste tombé sur cela aussi. Je devais changer le mode de vidage de OpenSessionInViewFilter de Spring en manuel, et tout à coup j'ai commencé à obtenir cette exception. J'ai découvert que les problèmes concernaient des méthodes qui n'étaient pas annotées comme @Transactional. Je suppose donc que Spring traite implicitement tout le code d'accès aux données en dehors de ces méthodes en lecture seule. L'annotation de la méthode a résolu le problème.

Une autre méthode consiste à appeler la méthode setCheckWriteOperations sur l'objet HibernateTemplate.

6
alh84001

Utilisez le bean ci-dessous pour HibernateTemplate in contexte d'application .

<bean id="template" class="org.springframework.orm.hibernate4.HibernateTemplate">  
        <property name="sessionFactory" ref="mysessionFactory"></property>
        <property name="checkWriteOperations" value="false"></property>
        </bean>
5
Amit Sharma

Essayez d'utiliser ceci

hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);

L'erreur devrait disparaître car le modèle ne vérifie pas si vous utilisez une transaction.

3
bigrowedogg

Vous devez oublier d'ajouter l'annotation @Transactional à votre classe/méthode de service DAO, cela spécifiera que votre opération de base de données sera gérée par une transaction gérée par un ressort. 

2
wise king

1) ajoutez @Transactional au-dessus de vos méthodes d'opération de base de données et 2) assurez-vous de disposer du gestionnaire de transations piloté par annotation 

ajouter

au-dessus de la configuration de HibernateTransactionManager dans le fichier applicationContext.xml

1
user3345582

Le morceau de code ci-dessous est travaillé pour moi.

hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);
1
user3198259

J'ai eu le même problème et après une journée d'enquête, j'ai observé la déclaration suivante

<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

J'ai enlevé 

mode="aspectj"

et le problème a disparu

0
Mihai