I have a below spring setup. Basically I am trying to configure two transaction managers here. One with hibernate and other with JPA. But somehow when I try to run JPA transaction manager, there I get "javax.persistence.TransactionRequiredException: No transactional EntityManager available" error. Appreciate if somebody finds the problem in below code.
data-context.xml file as below
<bean id="fundBO" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.test.FundBo"/>
<property name="interceptorNames">
<list>
<idref bean="transactionInterceptor"/>
<idref bean="fundBOTarget"/>
</list>
</property>
</bean>
<bean id="fundBOTarget" class="com.test.FundBoImpl" />
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<bean class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</property>
</bean>
And AppConfig as Below.
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan(basePackages = {"com.test.**"})
#ImportResource("classpath:data-context.xml")
public class AppConfig {
#Resource
DataSource dataSource;
#Bean
public JpaTransactionManager jpaTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
transactionManager.setJpaDialect(new HibernateJpaDialect());
transactionManager.setNestedTransactionAllowed(true);
transactionManager.afterPropertiesSet();
return transactionManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setPersistenceProviderClass(HibernatePersistence.class);
factoryBean.setPackagesToScan("com.test.**");
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.SQLServerDialect");
jpaProperties.put("hibernate.jdbc.batch_size", "20");
jpaProperties.put("hibernate.show_sql", "false");
jpaProperties.put("hibernate.hbm2ddl.auto", "validate");
jpaProperties.put("hibernate.autoReconnect", "true");
jpaProperties.put("hibernate.autoReconnectForPools", "true");
jpaProperties.put("hibernate.is-connection-validation-required", "true");
factoryBean.setJpaProperties(jpaProperties);
factoryBean.afterPropertiesSet();
return factoryBean;
}
}
Related
I'm having a hard time converting this spring xml into java config. How can I define an adviceChain bean base on the below spring file.
This's my xml file:
<util:list id="adviceChain">
<ref bean="retryInterceptor"/>
</util:list>
<bean id="retryInterceptor" class="org.springframework.amqp.rabbit.config.StatelessRetryOperationsInterceptorFactoryBean">
<property name="messageRecoverer">
<bean class="customDiscardingMessageRecoverer" />
</property>
<property name="retryOperations" ref="retryTemplate" />
</bean>
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.FixedBackOffPolicy">
<property name="backOffPeriod" value="3000" />
</bean>
</property>
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="3" />
</bean>
</property>
</bean>
Here is what I tried:
#Bean
StatelessRetryOperationsInterceptorFactoryBean retryInterceptor() {
StatelessRetryOperationsInterceptorFactoryBean retryInterceptor = new StatelessRetryOperationsInterceptorFactoryBean();
DiscardingMessageRecoverer messageRecoverer = new DiscardingMessageRecoverer();
retryInterceptor.setMessageRecoverer(messageRecoverer);
retryInterceptor.setRetryOperations(retryTemplate());
return retryInterceptor;
}
#Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(3000);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3);
retryTemplate.setRetryPolicy(retryPolicy);
return retryTemplate;
#Bean
SimpleMessageListenerContainer listenerContainer(){
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setConcurrentConsumers(1);
listenerContainer.setAdviceChain(new Advice[] { (Advice) retryInterceptor() });
listenerContainer.setMessageListener(listenerAdapter());
listenerContainer.setQueues(queue());
return listenerContainer;
}
I receive org.springframework.amqp.rabbit.config.StatelessRetryOperationsInterceptorFactoryBean$$EnhancerBySpringCGLIB$$e3de830c cannot be cast to org.aopalliance.aop.Advice error at the line listenerContainer.setAdviceChain(new Advice[] { (Advice) retryInterceptor() });
I think this's because I didn't define AdviceChain bean like the spring xml file but so far I don't know how.
Edit StatelessRetryOperationsInterceptorFactoryBean like this :
#Bean
RetryOperationsInterceptor retryInterceptor() {
StatelessRetryOperationsInterceptorFactoryBean retryInterceptor = new StatelessRetryOperationsInterceptorFactoryBean();
DiscardingMessageRecoverer messageRecoverer = new DiscardingMessageRecoverer();
retryInterceptor.setMessageRecoverer(messageRecoverer);
retryInterceptor.setRetryOperations(retryTemplate());
return retryInterceptor.getObject();
}
Here is my build.gradle...
dependencies {
compile('org.springframework.boot:spring-boot-starter-parent:2.0.1.RELEASE')
compile('org.springframework.boot:spring-boot-starter-batch')
compile("org.springframework.boot:spring-boot-starter-jdbc")
compile("org.springframework.boot:spring-boot-starter-web")
compile("com.h2database:h2")
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.batch:spring-batch-test')
}
Which I thought would enable JMX by default. I go to JConsole, connect to the application and expect to see a org.springframework.boot folder under java.util.logging, I see nothing.
So, now I pick a few of my custom beans and add #ManagedResource, I know see these.
However, what if I want to expose spring batch beans like #JobOperator how do I do this?
Pre Spring Boot, I could so something like:
<bean class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="spring:service=batch,bean=jobOperator">
<bean class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="jobOperator"/>
<property name="interceptorNames" value="exceptionTranslator" />
</bean>
</entry>
</map>
</property>
<property name="assembler">
<bean class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
<property name="interfaceMappings">
<map>
<entry key="spring:service=batch,bean=jobOperator"
value="org.springframework.batch.core.launch.JobOperator"/>
</map>
</property>
</bean>
</property>
</bean>
When I define my JobOperator, in my #Configuration file in Spring Boot I do:
#Bean
public JobOperator jobOperator() throws Exception {
SimpleJobOperator simpleJobOperator = new SimpleJobOperator();
// the operator wraps the launcher
simpleJobOperator.setJobLauncher(this.jobLauncher);
...
}
I can't add #ManagedResource under the #Bean annotation. So how do I expose the JobOperator as a JMX bean?
Here is how i did it in my code -
#Bean
public MBeanExporter exporter(){
MBeanExporter m = new MBeanExporter();
Map<String,Object> map = new HashMap<String, Object>();
JmxBean testBean = (JmxBean)ctx.getBean("testBean");
map.put("testBean",testBean);
m.setBeans(map);
return m;
}
#Bean
public JmxBean testBean(){
return new JmxBean("test",100);
}
In your case since you want to register jobOperator, just replace testBean in exporter bean with it.
I am having an issue with inserts not working. Reads (Selects) work okay. However, an insert is not being executed at end of my #Transactional method. Even though I do see the Transaction commit/close being called on the Entity Manager. I have tried different configurations and I still cannot get records (inserts) to work at the end of a transaction. And I don't see any errors being generated. I enabled the hibernate.transaction DEBUG and I don't see any hibernate transaction messages.
I am running Spring boot (.1.5.3) as a WAR executable with Tomcat. I am using persistence.xml (which is required by hibernate5-ddl-maven-plugin< to generate the SQL DDL during the build) on Spring Boot.
While debugging, I saw the JpaTransactionManager creating new transaction (Creating new transaction with name... Opened new EntityManager ,,,Exposing JPA transaction as JDBC transaction), joining other #Transactional methods (Found thread-bound EntityManager... Participating in existing transaction) and committing TX (Initiating transaction commit... Committing JPA transaction on EntityManager), and closing the JPA EM (Closing JPA EntityManager). This all happens according to the #Transactional specs. However, the record is not inserted into to the database. And I don't see any hibernate transaction messages.
Below is some of my configuration.
persistence.xml:
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<!--
This file is needed to generate the DDL.
-->
<persistence-unit
name="EzListaPersistence"
transaction-type="RESOURCE_LOCAL">
<description>
The set of entity types that can be managed by a
given entity manager is defined by a persistence unit. A
persistence unit defines the set of all classes that are
related or grouped by the application, and which must be
collocated in their mapping to a single data store.
</description>
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!--
List of fully qualified Entity Classes
-->
<class>com.ezlista.domain.account.Account</class>
...... NOT DISPLAYED ....
<class>com.ezlista.domain.useraccount.Notification</class>
<properties>
<!-- Hibernate Connection Settings -->
<property
name="hibernate.connection.provider_class"
value="org.hibernate.hikaricp.internal.HikariCPConnectionProvider" />
<property
name="hibernate.hikari.dataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" />
<property
name="hibernate.hikari.dataSource.url"
value="jdbc:mysql://localhost:3306/EZLISTA?verifyServerCertificate=false&useSSL=false" />
<property
name="hibernate.hikari.dataSource.user"
value="rubens" />
<property
name="hibernate.hikari.dataSource.password"
value="***MASKED***" />
<property
name="hibernate.hikari.dataSource.cachePrepStmts"
value="true" />
<property
name="hibernate.hikari.dataSource.prepStmtCacheSize"
value="250" />
<property
name="hibernate.hikari.dataSource.prepStmtCacheSqlLimit"
value="2048" />
<property
name="hibernate.hikari.minimumIdle"
value="5" />
<property
name="hibernate.hikari.maximumPoolSize"
value="10" />
<property
name="hibernate.hikari.idleTimeout"
value="30000" />
<!-- SQL Settings -->
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.show_sql"
value="false" />
<property name="hibernate.format_sql"
value="true" />
<property name="hibernate.use_sql_comments"
value="false" />
<property name="hibernate.ddl-auto"
value="none" />
<!-- Hibernate Cache Settings -->
<property name="hibernate.javax.cache.provider"
value="org.ehcache.jsr107.EhcacheCachingProvider" />
<property name="hibernate.cache.region.factory_class"
value="org.hibernate.cache.jcache.JCacheRegionFactory" />
<property name="hibernate.cache.use_second_level_cache"
value="true" />
<property name="hibernate.cache.use_query_cache"
value="false" />
<property name="hibernate.cache.use_structured_entries"
value="true" />
<property name="hibernate.cache.use_minimal_puts"
value="true" />
</properties>
</persistence-unit>
Java Configuration:
#EntityScan("com.ezlista.domain")
#EnableTransactionManagement
/**
* Spring Bootstrap Repository Configuration.
*
* #author Rubens Gomes
*/
#Configuration
public class RepositoryConfiguration
{
private final static Logger logger = LoggerFactory.getLogger(RepositoryConfiguration.class);
private final static String PERSISTENCE_UNIT = "EzListaPersistence";
#Inject
private Environment env;
public RepositoryConfiguration()
{
super();
logger.debug("Constructed");
}
// DataSource
#Bean(name = "dataSource")
public DataSource dataSource()
{
logger.info("Registering HikariDataSource bean.");
HikariDataSource ds = new HikariDataSource();
ds.setDataSourceClassName(env.getRequiredProperty("hikari.dataSourceClassName"));
ds.setMinimumIdle(env.getRequiredProperty("hikari.minimumIdle", Integer.class));
ds.setMaximumPoolSize(env.getRequiredProperty("hikari.maximumPoolSize", Integer.class));
ds.setIdleTimeout(env.getRequiredProperty("hikari.idleTimeout", Integer.class));
ds.setPoolName(env.getRequiredProperty("hikari.poolName"));
ds.addDataSourceProperty("user", env.getRequiredProperty("hikari.dataSource.user"));
ds.addDataSourceProperty("password", env.getRequiredProperty("hikari.dataSource.password"));
ds.addDataSourceProperty("databaseName", env.getRequiredProperty("hikari.dataSource.databaseName"));
ds.addDataSourceProperty("serverName", env.getRequiredProperty("hikari.dataSource.serverName"));
ds.addDataSourceProperty("cachePrepStmts", env.getRequiredProperty("hikari.dataSource.cachePrepStmts", Boolean.class));
ds.addDataSourceProperty("prepStmtCacheSize", env.getRequiredProperty("hikari.dataSource.prepStmtCacheSize", Integer.class));
ds.addDataSourceProperty("prepStmtCacheSqlLimit", env.getRequiredProperty("hikari.dataSource.prepStmtCacheSqlLimit", Integer.class));
return ds;
}
// EntityManagerFactory
#Bean(name = "entityManagerFactory")
public EntityManagerFactory entityManagerFactory()
{
logger.info("Registering EntityManagerFactory bean.");
JpaVendorAdapter hibernateJpavendorAdapter = new HibernateJpaVendorAdapter();
JpaDialect hibernateJpaDialect = new HibernateJpaDialect();
LocalContainerEntityManagerFactoryBean emfBean =
new LocalContainerEntityManagerFactoryBean();
emfBean.setJpaVendorAdapter(hibernateJpavendorAdapter);
emfBean.setJpaDialect(hibernateJpaDialect);
emfBean.setPersistenceUnitName(PERSISTENCE_UNIT);
emfBean.setDataSource(dataSource());
emfBean.afterPropertiesSet();
return emfBean.getObject();
}
// TransactionManager
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager()
{
logger.info("Registering JpaTransactionManager bean.");
JpaTransactionManager txManager = new JpaTransactionManager();
EntityManagerFactory emf = entityManagerFactory();
txManager.setEntityManagerFactory(emf);
txManager.setDataSource(dataSource());
return txManager;
}
}
Bingo!!!!
I annotated my EntityManager with #PersistenceContext(unitName = PERSISTENCE_UNIT_NAME). After that it works.
Here is the code that fixed the above problem. Notice the #PersistenceContext annotation below (that's what fixed the issue). Before I had #Inject annotation on the em.
#PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
protected EntityManager em;
I am new to Springs MVC and I tried to integrate HikariCP with JdbcTemplate. But got lot of errors any help, how to point JdbcTemplate to HikariCP DataSource.
in applicationContext.xml
<bean id="dataSource" class="com.zaxxer.hikari.HikariConfig">
<property name="poolName" value="springHikariCP" />
<property name="connectionTestQuery" value="SELECT 1" />
<property name="dataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" />
<property name="dataSourceProperties">
<props>
<prop key="url">${jdbc.url}</prop>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
</props>
</property>
</bean>
<bean id="jdbcTemplate" class="com.zaxxer.hikari.HikariDataSource" destroy- method="close">
<property name="dataSource" ref="dataSource"></property>
</bean>
And in controller:
Controller
public class HandleWareHouse{
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
#RequestMapping(value="/saveProduct_categories", method = RequestMethod.POST)
#ResponseBody
public String insertPc(#RequestParam Map<String,String> requestParams){
jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "INSERT INTO product_categories(name,code,des,grp_name) VALUES(?,?,?,?)";
jdbcTemplate.update(sql, new Object[] {requestParams.get("pname"),requestParams.get("pcode"),requestParams.get("stext"),requestParams.get("pcategory")});
return "sucess";
}
}
But getting error as shown below:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jdbcTemplate' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'com.zaxxer.hikari.HikariConfig' to required type 'javax.sql.DataSource' for property 'dataSource'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [com.zaxxer.hikari.HikariConfig] to required type [javax.sql.DataSource] for property 'dataSource': no matching editors or conversion strategy found
I have used spring javaconfig to configure datasource and getting jdbctemplate. I think this is the best
#Bean
public DataSource getDataSource() {
private HikariDataSource dataSource() {
final HikariDataSource ds = new HikariDataSource();
ds.setMaximumPoolSize(100);
ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
ds.setJdbcUrl("jdbc:oracle:thin:#localhost:1521:XE"); ;
ds.setUsername("username");
ds.setPassword("password");
return ds;
}
}
#Bean
public JdbcTemplate getJdbcTemplate() {
return new JdbcTemplate(getDataSource());
}
I hope this works well to you.
How can I setup application for given below java code?
Hashtable<String, String> properties = new Hashtable<String, String>();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.test.factory");
properties.put("com.domain", "DevelopmentDomain");
// properties.put(Context.PROVIDER_URL, "tcp://test:0506");
properties.put(Context.PROVIDER_URL, "tcp://10.00.0.00:0506");
properties.put(Context.SECURITY_PRINCIPAL, "aaa");
properties.put(Context.SECURITY_CREDENTIALS, "aaa");
javax.naming.Context context = new javax.naming.InitialContext(properties);
ConnectionFactory factory = (ConnectionFactory) context.lookup("ImpactPocQueueConnectionFactory");
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination queue = session.createQueue("test.producer");
I saw many examples here and there but none of them give complete picture.
If you want to translate this code literally to Spring configuration, consider taking advantage of #Configuration approach:
#Bean
public Destination queue() throws JMSException, NamingException {
return session().createQueue("test.producer");
}
#Bean
public Session session() throws JMSException, NamingException {
return connection().createSession(false, Session.AUTO_ACKNOWLEDGE);
}
#Bean(initMethod = "start")
public Connection connection() throws JMSException, NamingException {
return connectionFactory().createConnection();
}
#Bean
public ConnectionFactory connectionFactory() throws NamingException {
return (ConnectionFactory) context().lookup("ImpactPocQueueConnectionFactory");
}
#Bean
public Context context() throws NamingException {
return new javax.naming.InitialContext(properties());
}
#Bean
public Hashtable<String, String> properties() {
Hashtable<String, String> properties = new Hashtable<String, String>();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.test.factory");
properties.put("com.domain", "DevelopmentDomain");
//properties.put(Context.PROVIDER_URL, "tcp://test:0506");
properties.put(Context.PROVIDER_URL, "tcp://10.00.0.00:0506");
properties.put(Context.SECURITY_PRINCIPAL, "aaa");
properties.put(Context.SECURITY_CREDENTIALS, "aaa");
return properties;
}
Technically you can do all of this using XML, but I found this approach much more readable and maintainable. Now you have connectionFactory and queue beans available in scope. You can easily integrate with Spring JMS support. Let us know if you need further assitance.
Below given Spring applicationContext xml configuration is tested and working fine for me.
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">com.test.factory</prop>
<prop key="com.sonicsw.jndi.mfcontext.domain">DevelopmentDomain</prop>
<prop key="java.naming.provider.url">tcp://10.00.0.00:0506</prop>
<prop key="java.naming.security.principal">aaa</prop>
<prop key="java.naming.security.credentials">aaa</prop>
</props>
</property>
</bean>
<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate" ref="jndiTemplate"/>
<property name="jndiName" value="ImpactPocQueueConnectionFactory"/>
</bean>
<bean id="sonic" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="jmsQueueConnectionFactory"/>
</bean>
<route>
<from uri="seda:something"/>
<to uri="sonic:queue:queueName"/>
</route>