I'm using JP2 in my current web project. My main database holds the main entities. To connect on this DB i defined a Persitence Unit with a JTA Datasource:
Persistance.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="MyPU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>MyDB</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.logging.level" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
<property name="eclipselink.logging.logger" value="ServerLogger"/>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
</properties>
</persistence-unit>
</persistence>
and the JTA Datasource defined in sun-resources.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="org.postgresql.ds.PGSimpleDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="post-gre-sql_mydb_mypool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
<property name="serverName" value="localhost"/>
<property name="portNumber" value="5432"/>
<property name="databaseName" value="mydb"/>
<property name="User" value="myuser"/>
<property name="Password" value="mypass"/>
<property name="URL" value="jdbc:postgresql://localhost:5432/mydb"/>
<property name="driverClass" value="org.postgresql.Driver"/>
<property name="characterEncoding" value="UTF-8" />
</jdbc-connection-pool>
<jdbc-resource enabled="true" jndi-name="MyDB" object-type="user" pool-name="post-gre-sql_mydb_mypoll"/>
</resources>
And this is how i access the database on my DAO classes (witch are #ManagedBeans and #SessionScoped):
#ManagedBean(name = "pageDao")
#SessionScoped
public class PageDao implements Serializable {
#Resource
private UserTransaction utx = null;
#PersistenceUnit(unitName = "MyPU")
private EntityManagerFactory emf = null;
public EntityManager getEntityManager() {
return emf.createEntityManager();
}
public List<PageEnt> getAll() { ... }
public PageEnt getOne(long pageId) { ... }
public void addPage(PageEnt newPage) throws RollbackFailureException, PreexistingEntityException, Exception { ... }
public PageEnt update(PageEnt page) throws RollbackFailureException, NonexistentEntityException, Exception { ... }
public void remove(PageEnt page) throws RollbackFailureException, Exception { ... }
}
One of entities (customer) has properties for connecting on a separate (per-customer) database, witch are defined in run-time. These properties includes:
Databse name
Host and port
User and Password
My question are:
How do I efficiently create a database connection in run-time?
How can I create a new EntityManager from container-managed resources if there is no per-customer PersistanceUnit and Datasources defined (witch are defined at deploy-time)?
If i have to manually deal with the EntityManagerFactory (witch, as i learned in college, is a heavy and expansive object), how do I efficiently do that? Is there a good-practice or pattern?
How would the DAO pattern work? How do my DAO class will get the EntityManager?
Big thanks from Brazil.
It is possible to switch between multiple data sources at run time. It is provided by Spring AbstractRoutingDataSource . It is required to override the #determineCurrentLookupKey() method which will return a key to decide the specific datasource that is needed to be connected. Also there should be spring configuration that maps each of the possible keys and the corresponding data sources that are to be connected. Some thing like
<jee:jndi-lookup id="DataSource_Client1" jndi-name="DataSource_Client1" />
<jee:jndi-lookup id="DataSource_Client2" jndi-name="DataSource_Client" />
<bean id="DynamicDataSource" class="concrete implementation class name of AbstractRoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="Client1" value-ref="DataSource_Client1" />
<entry key="Client2" value-ref="DataSource_Client2" />
</map>
</property>
</bean>
A possible reference to this Dynamic DataSource
Hope this answers one of your questions
Related
I'm facing a problem in my project: entityManager.flush() is not doing anything, and the flushing is only being done right before commit, when exiting the EJB.
My project runs on WebSphere 7.
I'm using JPA2 through OpenJPA.
I'm using Spring for Autowiring.
I'm using Container Managed Transactions.
Relevant code snippets below
persistence.xml
<persistence-unit name="persistenceUnit" transaction-type="JTA">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<properties>
<property name="openjpa.TransactionMode" value="managed" />
<property name="openjpa.ConnectionFactoryMode" value="managed" />
<property name="openjpa.DynamicEnhancementAgent" value="true" />
<property name="openjpa.jdbc.DBDictionary" value="StoreCharsAsNumbers=false" />
<property name="openjpa.Log" value="SQL=TRACE"/>
</properties>
</persistence-unit>
applicationContext.xml
<!-- Configure a JPA vendor adapter -->
<bean id="openJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
</bean>
<!-- Entity Manager -->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/myappDS"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="openJpaVendorAdapter" />
</bean>
EJB3 Bean
#Stateless(name="JPABaseEntityServiceBean")
#Configurable
public class JPABaseEntityServiceBean implements JPABaseEntityService {
Logger logger = LoggerFactory.getLogger(JPABaseEntityServiceBean.class);
#Autowired
JPABaseEntityDao jpaBaseEntityDao;
public JPABaseEntity persist(JPABaseEntity jpaBaseEntity) {
return jpaBaseEntityDao.persist(jpaBaseEntity);
}
DAO:
#Repository
public class JPABaseEntityDao implements BaseEntityDao {
#PersistenceContext
transient EntityManager entityManager;
public JPABaseEntity persist(JPABaseEntity jpaBaseEntity) {
Date now = new Date();
jpaBaseEntity.setCreatedBy(TO_DO_ME);
jpaBaseEntity.setUpdatedBy(TO_DO_ME);
jpaBaseEntity.setUpdatedOn(now);
jpaBaseEntity.setCreatedOn(now);
entityManager.persist(jpaBaseEntity);
entityManager.flush();
return jpaBaseEntity;
}
The "INSERT" is being done only when leaving the EJB, meaning the entityManager.flush() inside the DAO is not working
Ok, resolved in a way
Seems like the problem was that the Entity Manager was not getting the Transaction from WebSphere (probably because the Entity Manager was being injected by Spring, I haven't investigated that deeply)
So what I did is make Spring control the transaction in the EntityManager:
1. added <tx:annotation-driven/> and <tx:jta-transaction-manager/> to applicationContext.xml
2. annotated the DAO methods with #Transactional
The overall transaction is still handled by the EJB, meaning it's still using CMT and JTA from WebSphere
I had a ton of problems in the way because of dependency hell (the one that got me the most was hibernate-core including JBoss's javax.transaction implementation, grr), but other than that everything seems to be working smoothly
I need to pass currently logged username to the DB Connection, because I want to trace user changes and store it in Oracle DB with correct user name. I am using Hibernate on my JBoss 5.1 and Oracle 11g database.
I think I need to do something like this (SOURCE):
try {
String e2eMetrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
e2eMetrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = System.getProperty("user.name");
((OracleConnection) conn).setEndToEndMetrics(e2eMetrics, (short) 0);
} catch (SQLException sqle) {
// Do something...
}
Problem:
The problem is that I do not have an idea how/where to call it to pass some value to the connection. I mean where, in which moment I should inject this code, because I am using hibernate and EntityManagerFactory and I do not create connection manually, so I do not have access to connection object itself. I need to call it only once for whole user connection and remove username after connection release.
Expected goal:
My goal is to pass String (actual username) from java code or hibernate configuration to Oracle DB under 'client_info' column in v$session table, just after/during connection establishment which is done by hibernate.
I know that the value can be passed by DBMS_APPLICATION_INFO.SET_CLIENT_INFO() procedure and I even checked it with code below, but the main question is how to call it only once for whole connection at connection establishment in hibernate?
Working Test for passing value to the v$session table to 'client_info' column:
#Override
#Transactional
public void setDBClientInfo(String clientInfo) {
try {
Query query = em.createNativeQuery("{call SYS.dbms_application_info.set_client_info(:userName)}")
.setParameter("userName", clientInfo);
query.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
}
My Configuration:
spring-servlet.xml:
<!-- DataSource -->
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MynameDS" expected-type="javax.sql.DataSource" />
<!-- EntityManagerFactory -->
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation" value="classpath:persistence.xml"/>
<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
</bean>
<!-- TransactionManager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf"/>
</bean>
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="MyUnitName" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>jdbc/MyNameDS</non-jta-data-source>
<class>com.mydomain.model.Assignment</class>
<class>com.mydomain.model.AuditLog</class>
<class>com.mydomain.model.Person</class>
<class>com.mydomain.model.Trial</class>
<class>com.mydomain.model.Violation</class>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<property name="hibernate.validator.apply_to_ddl" value="false"/>
<property name="hibernate.validator.autoregister_listeners" value="false"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
Is it possible to do via hibernate configuration or via Java code?
Please help me to solve this problem..
*when i deploy project in glassfish entitymanager is null.if i use another thing instead of RPC like servlet project work probably *
session bean is:
#Stateless
public class logic implements logicLocal {
#PersistenceContext(unitName="T2PU")
private EntityManager em;
#Override
public void addToDB(Test t){
em.persist(t);
}
}
and GWT RPC is :
public class MainRPCImpl extends RemoteServiceServlet implements MainRPC {
#EJB
logicLocal logic;
#Override
public String addToDB(Test t) {
String m="fail";
try {
logic.addToDB(t);
m="done successfuly";
} catch (Exception e) {
return m;
}
return m;
}
}
persistence unit is :
<persistence-unit name="T2PU" transaction-type="JTA">
<jta-data-source>acm</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
</persistence-unit>
</persistence>
and glassfish-resources
<resources>
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="mysql_acm_rootPool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
<property name="serverName" value="localhost"/>
<property name="portNumber" value="3306"/>
<property name="databaseName" value="acm"/>
<property name="User" value="root"/>
<property name="Password" value="1234"/>
<property name="URL" value="jdbc:mysql://localhost:3306/acm"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</jdbc-connection-pool>
<jdbc-resource enabled="true" jndi-name="acm" object-type="user" pool-name="mysql_acm_rootPool"/>
</resources>
specially thanks in advance.
Maybe there are different reasons, but don't you need to use the jdbc context prefix for your JNDI name in persistence.xml? Something like this:
<jta-data-source>jdbc/acm</jta-data-source>
From the Glassfish administration guide:
A JDBC resource is created by specifying the connection pool with
which the resource will be associated . Use a unique Java Naming and
Directory Interface (JNDI) name to identify the resource. ... Because
all JNDI names are in the java:comp/env subcontext, when specifying
the JNDI name of a JDBC resource in the Administration Console, use
only the jdbc/name format.
Our application use IBM WAS6.1 container with EJB3.0 feature pack. Transactions are container managed via EJB3.0 transactions started from ejb service bean. We use Spring for DI. There are 3 layers. Spring DAO, Spring Service and EJB3.0 Bean Service.
Now i when i try to use OpenJPA with WAS6.1. EntityManager is successfully injected into SpringDAO but container closes EntityManager before transaction commits. (Datasource is JNDI datasource)
Here is the stacktrace:
[1/4/12 9:23:17:769 EET] 0000001f ExceptionUtil E CNTR0020E: EJB threw an unexpected (non-declared) exception during invocation of method "inquiryReconcilation" on bean "BeanId(PaymentSystemAppV1#PaymentSystemServiceEjbV1.jar#PaymentServiceBean, null)". Exception data: <openjpa-1.0.3-SNAPSHOT-r420667:649224 fatal user error> org.apache.openjpa.persistence.InvalidStateException: You have closed the EntityManager, though the persistence context will remain active until the current transaction commits.
at org.apache.openjpa.persistence.EntityManagerImpl.assertNotCloseInvoked(EntityManagerImpl.java:1068)
Our persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="PaymentSystemEntityV1" transaction-type="JTA">
<jta-data-source>paymentDsSrv</jta-data-source>
<mapping-file>META-INF/namedQueries.xml</mapping-file>
/* entities */
<properties>
<property name="openjpa.TransactionMode" value="managed" />
<property name="openjpa.ConnectionFactoryMode" value="managed" />
<property name="openjpa.jdbc.DBDictionary" value="db2" />
<property name="openjpa.jdbc.Schema" value="PAYMENT"/>
<property name="openjpa.Log" value="DefaultLevel=INFO, Runtime=INFO, Tool=INFO, SQL=TRACE"/>
</properties>
</persistence-unit>
Spring Context:
<bean id="dbsJpaDao" parent="daoBase" class="com.kavuntek.dds.dao.impl.DbsJpaDaoImpl" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="entityManagerFactory" class="javax.persistence.Persistence"
factory-method="createEntityManagerFactory" >
<constructor-arg type="java.lang.String" value="PaymentSystemEntityV1"/>
</bean>
DAO:
public class DbsJpaDaoImpl implements IDbsJpaDao{
#PersistenceContext
private EntityManager entityManager;
public List<DbsLimit> subscriberLimitInquiry(){
...
return entityManager.createQuery(queryString);
}
EJB3.0 Service Bean:
#Stateless
#TransactionAttribute(TransactionAttributeType.REQUIRED)
#TransactionManagement(value = TransactionManagementType.CONTAINER)
#Interceptors(SpringBeanAutowiringInterceptor.class)
#Local(IDDSServiceV1.class)
public class DDSServiceBean iplements IDDSServiceV1{
#Autowired
private IDDSService ddsService;
public List<DbsInvoice> subscriberLimitInquiry()
...
I tried also spring LocalContainerEntityManagerFactory with these parameters:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
<property name="persistenceUnitName" value="PaymentSystemEntityV1"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter"/>
</property>
</bean>
Because transactions are not Spring managed, using WebSphereUowTransactionManager matters?
Nothing changed.
<bean id="transactionManager"
class="org.springframework.transaction.jta.WebSphereUowTransactionManager"/>
Any opinions?
Thanks.
P.S. When i call entityManager.getTransaction() in DAO i get:
Exception data: java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
I found some strange behavior when using nested Spring transactions: when, in the same class, a method annotated as #Transactional calls another method also annotated as #Transactional the second annotation is not used.
Let's consider the following class:
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
final Main main = context.getBean(Main.class);
// First Op
System.out.println("Single insert: " + main.singleInsert());
// Second Op
main.batchInsert();
// Third Op
main.noTransBatchInsert();
}
#PersistenceContext
private EntityManager pm;
#Transactional(propagation=Propagation.REQUIRED)
public void batchInsert() {
System.out.println("batchInsert");
System.out.println("First insert: " + singleInsert());
System.out.println("Second insert: " + singleInsert());
}
public void noTransBatchInsert() {
System.out.println("noTransBatchInsert");
System.out.println("First insert: " + singleInsert());
System.out.println("Second insert: " + singleInsert());
}
#Transactional(propagation=Propagation.REQUIRES_NEW)
public int singleInsert() {
System.out.println("singleInsert");
Pojo p = new Pojo();
pm.persist(p);
return p.getId();
}
}
The entity if the following class:
#Entity
public class Pojo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Override
public String toString() {
return "Pojo: " + id;
}
public int getId() {
return id;
}
}
and the String parts applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee"
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/jee http://www.springframework.org/schema/jee/spring-jee-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">
<tx:annotation-driven />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="MyPersistenceUnit" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
</beans>
and the configuration class (I could have merge this in applicationContext.xml).
#Configuration
#ImportResource("/META-INF/applicationContext.xml")
public class Config {
#Bean
public Main main() {
return new Main();
}
}
For completeness the persistence.xml file:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">
<persistence-unit name="MyPersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<property name="hibernate.connection.driver_class" value="org.h2.Driver" />
<property name="hibernate.connection.url" value="jdbc:h2:mem:TestDSJPA2;DB_CLOSE_DELAY=-1;LOCK_MODE=0" />
<!--<property name="hibernate.connection.url" value="jdbc:h2:mem:TestDSJPA2;DB_CLOSE_DELAY=-1;LOCK_MODE=0" />-->
<property name="hibernate.connection.username" value="sa" />
<property name="hibernate.connection.password" value="" />
<property name="hibernate.connection.autocommit" value="false"/>
<property name="hibernate.c3p0.min_size" value="5" />
<property name="hibernate.c3p0.max_size" value="20" />
<property name="hibernate.c3p0.timeout" value="300" />
<property name="hibernate.c3p0.max_statements" value="50" />
<property name="hibernate.c3p0.idle_test_period" value="3000" />
</properties>
</persistence-unit>
</persistence>
So in the main class, the first operation is performed as expected that is in a new transaction. The output (including some DEBUG messages) is:
DEBUG o.h.transaction.JDBCTransaction - begin
singleInsert
DEBUG o.h.transaction.JDBCTransaction - commit
Single insert: 1
The second operation gives the following output:
batchInsert
singleInsert
DEBUG o.h.transaction.JDBCTransaction - begin
First insert: 2
singleInsert
Second insert: 3
DEBUG
This is not what I expected since in annotating singleInsert with #Transactional(propagation=Propagation.REQUIRES_NEW) I would expect a new transaction to be created for every call which is not what's happening since the same top level transaction is used for both insertion.
The third operation fails as well as no transaction is created at all:
noTransBatchInsert
singleInsert
DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress
First insert: 0
singleInsert
DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress
Second insert: 0
In the #Configuration beans Spring ensures that calls to the method on the same class are proxified which is obviously not happening here. Is there a way do change this behavior?
This behavior is the documented behavior of Spring when using the proxy mode for AOP. It can be changed by switching to the aspectj mode which perform code instrumentation either on compilation or at runtime.
This is not specifically a problem with #Transactional. It is due to the configuration of your <tx:annotation-driven/>.
Spring uses two different AOP mechanisms: JDK dynamic proxies or CGLIB. JDK dynamic proxies is the default and it works through the use of interfaces at run-time. CGLIB works by generating subclasses at compile-time. If you specify <tx:annotation-driven proxy-target-class="true"/>, Spring will use CGLIB, and your second #Transactional will fire.
You can read more about the subject here.
The default advice mode for processing #Transactional annotations is
proxy, which allows for interception of calls through the proxy only.
Local calls within the same class cannot get intercepted that way. For
a more advanced mode of interception, consider switching to aspectj
mode in combination with compile-time or load-time weaving.
Taken from Spring reference. https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#tx-propagation-nested