is it possible to define a bean with the use of static final fields of CoreProtocolPNames class like this:
<bean id="httpParamBean" class="org.apache.http.params.HttpProtocolParamBean">
<constructor-arg ref="httpParams"/>
<property name="httpElementCharset" value="CoreProtocolPNames.HTTP_ELEMENT_CHARSET" />
<property name="version" value="CoreProtocolPNames.PROTOCOL_VERSION">
</bean>
public interface CoreProtocolPNames {
public static final String PROTOCOL_VERSION = "http.protocol.version";
public static final String HTTP_ELEMENT_CHARSET = "http.protocol.element-charset";
}
If it is possible, what is the best way of doing this ?
Something like this (Spring 2.5)
<bean id="foo" class="Bar">
<property name="myValue">
<util:constant static-field="java.lang.Integer.MAX_VALUE"/>
</property>
</bean>
Where util namespace is from xmlns:util="http://www.springframework.org/schema/util"
But for Spring 3, it would be cleaner to use the #Value annotation and the expression language. Which looks like this:
public class Bar {
#Value("T(java.lang.Integer).MAX_VALUE")
private Integer myValue;
}
Or, as an alternative, using Spring EL directly in XML:
<bean id="foo1" class="Foo" p:someOrgValue="#{T(org.example.Bar).myValue}"/>
This has the additional advantage of working with namespace configuration:
<tx:annotation-driven order="#{T(org.example.Bar).myValue}"/>
don't forget to specify the schema location..
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">
</beans>
One more example to add for the instance above. This is how you can use a static constant in a bean using Spring.
<bean id="foo1" class="Foo">
<property name="someOrgValue">
<util:constant static-field="org.example.Bar.myValue"/>
</property>
</bean>
package org.example;
public class Bar {
public static String myValue = "SOME_CONSTANT";
}
package someorg.example;
public class Foo {
String someOrgValue;
foo(String value){
this.someOrgValue = value;
}
}
<util:constant id="MANAGER"
static-field="EmployeeDTO.MANAGER" />
<util:constant id="DIRECTOR"
static-field="EmployeeDTO.DIRECTOR" />
<!-- Use the static final bean constants here -->
<bean name="employeeTypeWrapper" class="ClassName">
<property name="manager" ref="MANAGER" />
<property name="director" ref="DIRECTOR" />
</bean>
Related
How to configure Gson before constructing a GsonHttpMessageConverter?
I need to use #Expose and specify Date format.
(New Method) Using Java Config
Extend WebMvcConfigurerAdapter or if you need more control use WebMvcConfigurationSupport.
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(createGsonHttpMessageConverter());
super.configureMessageConverters(converters);
}
private GsonHttpMessageConverter createGsonHttpMessageConverter() {
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'")
.create();
GsonHttpMessageConverter gsonConverter = new GsonHttpMessageConverter();
gsonConverter.setGson(gson);
return gsonConverter;
}
}
You can read more on how to customize provided configuration.
(Old Method) Using XML configuration
In DispatcherServlet context:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="gsonBuilder" class="com.google.gson.GsonBuilder">
<property name="dateFormat" value="yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'" />
</bean>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="gsonBuilder" />
<property name="targetMethod" value="excludeFieldsWithoutExposeAnnotation" />
</bean>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.GsonHttpMessageConverter">
<property name="gson">
<bean class="com.google.gson.Gson" factory-bean="gsonBuilder" factory-method="create" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>
I am using Spring Framework / Data / HATEOAS and trying to add Dozer.
I have the following bean in my spring-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:data="http://www.springframework.org/schema/data/jpa"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="POSTGRESQL" />
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
<property name="jpaDialect" ref="jpaDialect" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost:5432/cp" />
<property name="username" value="cp_user" />
<property name="password" value="+JMJ+pw0m2d" />
</bean>
<context:component-scan base-package="com.mydomain.data.assembler" />
<data:repositories base-package="com.mydomain.repository" />
<mvc:annotation-driven />
<bean id="dozerFactory" class="org.dozer.spring.DozerBeanMapperFactoryBean" scope="singleton">
<property name="mappingFiles" value="classpath*:/*mapping.xml"/>
</bean>
</beans>
And the following assembler:
#Component
public class UserResourceAssembler {
#Inject
private Mapper dozerBeanMapper;
public UserResource toResource(User user) {
UserResource resource = dozerBeanMapper.map(user, UserResource.class);
resource.add(linkTo(methodOn(UserController.class).get(user.getId())).withSelfRel());
return resource;
}
public User toEntity(UserResource resource) {
User user = dozerBeanMapper.map(resource, User.class);
return user;
}
}
So, - I'm very new to beans and injection - but I guess that the factory bean is ?supposed? to inject the Mapper. But the Mapper is definitely null. I know I'm not doing this right, but what am I doing wrong?
Spring injects its beans into spring managed beans. You are using an unmanaged static context. Change UserResourceAssembler into a managed bean as well:
#Component
public class UserResourceAssembler {
#Inject
private Mapper dozerBeanMapper;
public UserResource toResource(User user) {
}
public User toEntity(UserResource resource) {
}
}
See why can't we autowire static fields in spring.
I would have preferred something like the above. But then I read:
Dozer Singleton Startup Bean injetced as Null
That worked. Here was my implementation.
I removed the bean from spring-config, and the context scan.
I added this class:
#Singleton
public class DozerInstantiator {
public static DozerBeanMapper getInstance(){
return MapperHolder.instance;
}
private static class MapperHolder{
static final DozerBeanMapper instance = new DozerBeanMapper();
}
}
And updated my assembler like this:
public class UserResourceAssembler {
private DozerBeanMapper mapper;
public UserResourceAssembler() {
mapper = DozerInstantiator.getInstance();
}
public UserResource toResource(User user) {
UserResource resource = mapper.map(user, UserResource.class);
resource.add(linkTo(methodOn(UserController.class).get(user.getId())).withSelfRel());
return resource;
}
public User toEntity(UserResource resource) {
User user = mapper.map(resource, User.class);
return user;
}
}
I am using Unitils with Spring for unit testing. I've configured Spring with datasource using a properties file.
My question is how can I use the same datasource or the same properties for Unitils?
Unitils expects a file in the classpath unitils.properties with database configuration parameters like url, user, password and driver.
I've tried to configure Unitils using the properties used in the Spring configuration as below but it is not working.
database.driverClassName=${jdbc.driver.class}
Thanks,
Adi
One potential solution... You could have your Spring configuration read its datasource parameters from the unitils.properties, instead of the other way around. Probably not ideal.
I believe unitils is using spring under the covers, so you might also try adding your datasource context in your unitils tests by using #SpringApplicationContext. If you could figure out the name of the datasource bean setup by unitils when it starts up, you could override it in your context (assuming the unitils datasource bean is created before the other spring beans are which may/may not be true.)
e.g.
#SpringApplicationContext({"correctDataSourceContext.xml"})
EDIT: Another option that will definitely work: https://stackoverflow.com/a/6561782/411229
Basically instantiate Unitils yourself and set the properties manually.
Ryan answer is correct and helpful as well though I've used different approach.
I extended the class PropertiesDataSourceFactory ro override the methods as follows:
public class UnitilsDataSourceFactory extends PropertiesDataSourceFactory {
#Override
public void init(Properties configuration) {
try {
String[] configFiles = new String[] { "applicationContext-test.xml" };
BeanFactory factory = new ClassPathXmlApplicationContext(configFiles);
SystemPropertiesReader systemPropertiesReader = (SystemPropertiesReader) factory.getBean("systemPropertiesReader");
Properties loadProperties = systemPropertiesReader.loadProperties();
super.init(loadProperties);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public DataSource createDataSource() {
DataSource dataSource = super.createDataSource();
return dataSource;
}
}
and also wrote a SystemPropertiesReader as:
public class SystemPropertiesReader {
private Collection<Resource> resources;
public void setResources(final Collection<Resource> resources) {
this.resources = resources;
}
public void setResource(final Resource resource) {
resources = Collections.singleton(resource);
}
#PostConstruct
public Properties loadProperties() throws Exception {
final Properties systemProperties = System.getProperties();
for (final Resource resource : resources) {
final InputStream inputStream = resource.getInputStream();
try {
systemProperties.load(inputStream);
} finally {
//
}
}
return systemProperties;
}
}
and added a bean with the properties file:
<bean id="systemPropertiesReader" class="uk.co.friendslife.eventmanager.domain.dao.SystemPropertiesReader">
<property name="resource">
<value>classpath:/META-INF/em/config/eventmanager_${database_name_lower}.properties</value>
</property>
</bean>
add the following to unitils.properties:
org.unitils.database.config.DataSourceFactory.implClassName=x.y.UnitilsDataSourceFactory
Just want to add some idea and im not sure if it is a best practice or not so correct me if theres something wrong.
MYPROJECT
-src
--TestPackage
---BaseServiceTest.class
---BlogspotServiceTest.class
--hibernate.cfg.xml
-web
--WEB-INF
---blogspot-servlet-test.xml
---jdbc-test.properties
in my case I used my blogspot-servlet-test.xml to call or to create the datasource
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
.... some bean configuration
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="file:web/WEB-INF/jdbc.properties"/>
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.databaseurl}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- DAO'S -->
<bean id="blogspotDAO" class="package.BlogspotDAOImpl"/>
<!-- SERVICES -->
<bean id="blogspotService" class="package.BlogspotServiceImpl"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
MY jdbc-test.properties file
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.dialect=org.hibernate.dialect.MySQL5Dialect
jdbc.databaseurl=jdbc:mysql://127.0.0.1:3306/dbspringminiblogtest
jdbc.username=root
jdbc.password=
For hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd//hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<mapping class="somePackage.entity.Author"/>
<!-- Other Entity Class to be mapped -->
</session-factory>
</hibernate-configuration>
and i created BaseClass for me to lessen creating of multiple #SpringApplicationContext annotation and it is also use to configure common configuration needed in testing other class, just extends it.
#SpringApplicationContext({"file:web/WEB-INF/blogspot-servlet-test.xml"})
public class BaseServiceTest extends UnitilsJUnit4 {
}
i used #SpringApplicationContext to load the datasource and other bean configurations on my BaseClass and this is how i implement it.
Below : see Spring-Unitils Tutorial
for more details
public class BlogspotServiceTest extends BaseServiceTest{
#Mock
#InjectInto(property = "blogspotDAO")
#SpringBean("blogspotDAO")
private BlogspotDAO blogspotDAOMock;
#TestedObject
#SpringBean("blogspotService")
private BlogspotService blogspotServiceMock;
#Test
public void testAddBlogSpot() {
assertNotNull("BlogspotService Not null",blogspotServiceMock);
}
}
NOTE: please create unitils.properties and unitils-local.properties inside TestPackage to be able to run the program.
For #SpringBean explanation and other annotation please read :
Unitils-EasyMock
My applicationContext.xml is in path:
src/main/resources/META-INF/spring
and property files are in path:
src/main/resources/messages
and I load spring context in web.xml as follows:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:META-INF/spring/applicationContext.xml</param-value>
</context-param>
when I am configuring MessageSource and PropertyPlaceholderConfigurer as follows:
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:messages/apps.properties</value>
</list>
</property>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:messages/ValidationMessages</value>
<value>classpath:messages/app</value>
</list>
</property>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
they both don't work, it only works when I change classpath to classpath*
Any ideas why?
From Spring documentation:
4.7.2.2 The classpath*: prefix
[...] location string may use the special classpath*: prefix: [...]
This special prefix specifies that all classpath resources that match the given name must be obtained [...], and then merged to form the final application context definition.
Are you sure there are no other messages/apps.properties files on your CLASSPATH coincidentally taking precedence and overriding your file? This description suggests that you might have several same named files that are merged when * is used.
Can you check this by calling:
SomeClass.class.getClassLoader().getResources("/messages/apps.properties");
?
Look at this excellent article for classpath v classpth* difference in regards to spring resource loading, I did some testing on your problem in my testing it worked whether i used classpath or classpath*
I am listing the code here about the test i did
I created a this directory structure (META-INF/spring under src/main/resource) placed context.xml
I am listing complete context.xml here
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="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">
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:messages/apps.properties</value>
</list>
</property>
</bean>
<bean class="prasanna.service.TestBean">
<property name="appName" value="${appname}"></property>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:messages/ValidationMessages</value>
<value>classpath:messages/apps</value>
</list>
</property>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
</beans>
Listing for apps.properties
appname=spring mvc app
Listing for ValidationMessages.properties
error.name=Invalid name
TestBean is rather simple
public class TestBean
{
private String appName;
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
}
I am using a simple java class to load the property files and read them
public class LoadContext
{
public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:META-INF/spring/context.xml"});
ReloadableResourceBundleMessageSource msgs = ctx.getBean(ReloadableResourceBundleMessageSource.class);
TestBean testBean = ctx.getBean(TestBean.class);
Assert.assertTrue(testBean.getAppName().equals("spring mvc app"));
String msg = msgs.getMessage("appname", new Object[]{new DefaultMessageSourceResolvable("appname")}, null);
System.out.println(" "+ msg);
String msg2 = msgs.getMessage("error.name", new Object[]{new DefaultMessageSourceResolvable("error.name")}, null);
System.out.println(" "+ msg2);
}
}
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