1

I'm trying to execute a query which has to delete items from a sub select.

Environment :

  • Wildfly 8.2
  • Spring Framework 4.1.6
  • Spring Data 1.8.0
  • Hibernate 4.3.10
  • MySQL 5

Here's the repository :

public interface MySecondEntityRepository extends JpaRepository<MySecondEntity, UUID> {
    @Modifying
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Query("DELETE FROM MySecondEntity e WHERE e.uuid IN ("
             + "SELECT e2.uuid "
             + "FROM MySecondEntity e2, MyEntity e1 "
             + "WHERE e2.entityUUID = e1.uuid "
             + "AND e1.uuid = :uuid "
             + "AND e1.*** = ..."
             + "AND e1.*** = ..."
         + ")")
    public void deleteByEntityUUID(@Param("uuid") UUID entityUUID, ...);
}

Here's the transaction manager's XML definition :

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

where entityManagerFactory is a LocalContainerEntityManagerFactoryBean.

I'm using them not to be dependent of the provider.

In the logs, I can see :

SQL | insert into HT_my_entity select ...
SQL | delete from my_entity where uuid IN (select uuid FROM HT_my_entity);

When I execute manually the select ..., I get the correct uuids to be deleted. but when spring/hibernate execute them, the transaction does not commit even with Propagation.REQUIRES_NEW. No exception thrown.

Full logs :

14:41:45,073 | DEBUG | AnnotationTransactionAttri:108  | Adding transactional method 'DefaultJpaRepositoryImpl.deleteByEntityUUID' with attribute: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
14:41:45,073 | DEBUG | JpaTransactionManager     :367  | Creating new transaction with name [xxx.spring.jpa.factory.DefaultJpaRepositoryImpl.deleteByEntityUUID]: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
14:41:45,074 | DEBUG | JpaTransactionManager     :371  | Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@cd414] for JPA transaction
14:41:45,075 | DEBUG | AbstractTransactionImpl   :160  | begin
14:41:45,075 | DEBUG | LogicalConnectionImpl     :226  | Obtaining JDBC connection
14:41:45,075 | DEBUG | LogicalConnectionImpl     :232  | Obtained JDBC connection
14:41:45,076 | DEBUG | JdbcTransaction           :69   | initial autocommit status: true
14:41:45,076 | DEBUG | JdbcTransaction           :71   | disabling autocommit
14:41:45,076 | DEBUG | JpaTransactionManager     :403  | Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@7d7135]
14:41:45,078 | DEBUG | JpaTransactionManager     :334  | Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@cd414] for JPA transaction
14:41:45,079 | DEBUG | JpaTransactionManager     :417  | Suspending current transaction, creating new transaction with name [xxx.spring.jpa.factory.DefaultJpaRepositoryImpl.deleteByEntityUUID]
14:41:45,082 | DEBUG | JpaTransactionManager     :371  | Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@35e880] for JPA transaction
14:41:45,082 | DEBUG | AbstractTransactionImpl   :160  | begin
14:41:45,082 | DEBUG | LogicalConnectionImpl     :226  | Obtaining JDBC connection
14:41:45,083 | DEBUG | LogicalConnectionImpl     :232  | Obtained JDBC connection
14:41:45,083 | DEBUG | JdbcTransaction           :69   | initial autocommit status: true
14:41:45,083 | DEBUG | JdbcTransaction           :71   | disabling autocommit
14:41:45,084 | DEBUG | JpaTransactionManager     :403  | Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@1b46a77]
14:41:45,223 | DEBUG | SQL                       :109  | insert into HT_my_entity select my_second_0_.uuid as uuid from my_second_entity my_second_0_ where my_second_0_.uuid in (select my_second_1_.uuid from my_second_entity my_second_1_ cross join my_entity my_entity_2_ where my_second_1_.entity_uuid=my_entity_2_.uuid and my_entity_2_.uuid=?)
14:41:45,232 | DEBUG | SQL                       :109  | delete from my_entity where uuid IN (select uuid FROM HT_my_entity);
14:41:45,333 | DEBUG | JpaTransactionManager     :755  | Initiating transaction commit
14:41:45,334 | DEBUG | JpaTransactionManager     :512  | Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@35e880]
14:41:45,335 | DEBUG | AbstractTransactionImpl   :175  | committing
14:41:45,388 | DEBUG | JdbcTransaction           :113  | committed JDBC Connection
14:41:45,389 | DEBUG | JdbcTransaction           :126  | re-enabling autocommit
14:41:45,393 | DEBUG | JpaTransactionManager     :600  | Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@35e880] after transaction
14:41:45,394 | DEBUG | EntityManagerFactoryUtils :432  | Closing JPA EntityManager
14:41:45,396 | DEBUG | LogicalConnectionImpl     :246  | Releasing JDBC connection
14:41:45,397 | DEBUG | LogicalConnectionImpl     :264  | Released JDBC connection

What I tried :

  • Propagation.REQUIRED and Propagation.REQUIRES_NEW
  • .flush() after the method's call
  • Unwrap the subquery to test it. It works correctly.
  • Update Spring Data / Hibernate

Note :

  • I do not include MySecondEntity in MyEntity for performance reason. A MyEntity can have 10k+ MySecondEntity. I know I can use lazy fetching but actually, entities are mapped to data transfert object and this cause issues to mapping process. Also, I do not want the retrieve all the data to delete them.
  • <tx:annotation-driven/> is defined
  • <aop:aspectj-autoproxy/> is defined
  • The method is called directly from a @RestController.
Ludovic Guillaume
  • 3,237
  • 1
  • 24
  • 41
  • Second option : the transactions ends silently / no changes in db. – Ludovic Guillaume Jun 01 '15 at 11:59
  • Are the entities being deleted versioned? Meaning, does the actaul SQL contain version comparison in the where clause? – Dragan Bozanovic Jun 01 '15 at 12:00
  • No, the entities are not versioned but they are audited by using `@EntityListeners` (and on fields : `@CreatedDate`, `@CreatedBy`, `@LastModifiedDate`, `@LastModifiedBy`). – Ludovic Guillaume Jun 01 '15 at 12:02
  • You should debug the outcome of the delete statement when executed by Hibernate (are any rows deleted). I suggest setting org.hibernate log level to TRACE while investigating this. I don't think that transaction is not committed , I would rather say that no rows were selected by the where clause in the subquery (maybe parameters are not bound properly by Spring Data or Hibernate). – Dragan Bozanovic Jun 01 '15 at 12:23
  • I added full logs with `org.hibernate` / `org.springframework.transaction` log level to `TRACE`. As it says, the transaction is committed but related rows are not deleted. My first thought was what you said : no rows are selected by the subquery. That's why I tested manually but I successfuly get the rows. How can I see if parameters are not bound properly? – Ludovic Guillaume Jun 01 '15 at 12:50
  • I unwrapped the subquery to a test method. It lists all the rows so I assume the parameters are properly bound and the subquery works. – Ludovic Guillaume Jun 01 '15 at 12:56
  • I also tried to return the rows affected count, it gives me to correct value... I really don't understand this. – Ludovic Guillaume Jun 01 '15 at 13:10
  • How did you check the count of updated rows for `delete from my_entity where uuid IN (select uuid FROM HT_my_entity)`? I would execute this in debug mode stepping through Hibernate source if there is nothing in the logs... – Dragan Bozanovic Jun 01 '15 at 14:37
  • It's not directly on this query but on the repository's method where I changed `void` to `int`. I read on another post (I don't find it anymore -_-) that the query I'm trying to do have issues with MySQL. Actually, I don't find any `CREATE TABLE HT_my_entity` in the log (not even at initialization). I suppose that the table isn't created and don't throw any exception when inserting. I'll try stepping in debug mode. – Ludovic Guillaume Jun 01 '15 at 14:56
  • Exactly, that was my concern as well. Maybe the data from the temporary table are not visible or don't exist when the delete statement is executed. You can check when Hibernate creates the temporary table by putting a breakpoint in MySQLDialect.getCreateTemporaryTableString. Also, make sure you have configured Hibernate to use MySQL5Dialect for MySQL 5.x – Dragan Bozanovic Jun 01 '15 at 15:29
  • When debugging step by step, I can see that the insert returns the correct rows count (in `org.hibernate.hql.spi.TableBasedDeleteHandlerImpl` line 144). I tried to `SELECT * FROM HT_my_entity` but MySQL don't find it. During the debug, I did not find any `CREATE TABLE`... I'll try again tomorrow. Thanks for your help. – Ludovic Guillaume Jun 01 '15 at 15:49
  • `delete from my_entity where uuid IN (select uuid FROM HT_my_entity)` has no rows updated but the `insert into HT_` has. I'm using `MySQL5Dialect`. `getCreateTemporaryTableString` is called at the initialization for each defined table – Ludovic Guillaume Jun 02 '15 at 08:09
  • How did you configure read (select) privileges for the user/database that you connect with? Does user have select privileges for all the objects in the schema? Or you have enabled it selectively, for example per table basis? – Dragan Bozanovic Jun 02 '15 at 08:49
  • There's no specific privileges. I use root for my dev environment. – Ludovic Guillaume Jun 02 '15 at 09:20
  • Then I'm out of the options. :( You could take a look at this [question](http://stackoverflow.com/questions/23041964/jpa-2-1-hibernate-4-3-deprecation-warning), maybe it could have something to do with your issue as well. – Dragan Bozanovic Jun 02 '15 at 10:18
  • Dude, thanks a lot anyway. Actually, I don't have any warning linked to this issue. I tried Hibernate 5.0.0.CR1 but I had issues w/ Envers. I'll wait for Wildfly 9.0 Final and Hibernate 5.0.0.Final. I'm pretty sure this comes from one of both. I had no issue when using this king of query with Glassfish and Eclipselink. – Ludovic Guillaume Jun 02 '15 at 10:53

0 Answers0