I'm trying to setup a Tomcat connection pool (to MySQL) in my Java web app, while using the Spring JDBCTemplate.
This is the Java class to create the connection pool:
#Configuration
public class DataAccessConfiguration {
#Bean(destroyMethod = "close")
public javax.sql.DataSource datasource() {
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:java-config");
ds.setUsername("sa");
ds.setPassword("");
ds.setInitialSize(5);
ds.setMaxActive(10);
ds.setMaxIdle(5);
ds.setMinIdle(2);
return ds;
}
#Bean public JdbcOperations tpl() {
return new JdbcTemplate(datasource());
}
}
This is how I get the ApplicationContext (in the main method for example):
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
How should I define the DataAccessConfiguration class in the Beans.xml file so Spring knows to use it?
**
Update:
**
This is the actual configuration method:
public javax.sql.DataSource datasource() {
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/mysql");
p.setDriverClassName("com.mysql.jdbc.Driver");
p.setUsername("root");
p.setPassword("");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors(
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
ds.setPoolProperties(p);
return ds;
}
Can you please help rewrite in the Beans.xml?
For the values passed in Properties setter methods exist. Therefore XML-based bean definition goes like:
<bean name="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
<property name="url" value="jdbc:mysql://localhost:3306/mysql"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
....
</bean>
<bean name="tpl" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="jdbcUrl" value="#{config['db.url']}" />
<property name="driverClass" value="#{config['db.driver']}" />
<property name="user" value="#{config['db.username']}" />
<property name="password" value="#{config['db.password']}" />
<property name="acquireIncrement" value="1" />
<property name="idleConnectionTestPeriod" value="300" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="20" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
Related
I am fairly new to the Spring Framework and have been having some trouble setting up the project I am currently working on. I need to be able to connect to two different databases one being MongoDB and the other being MSSQL. I am using JPA to connect to the MSSQL.
The problem that I am encountering is that it appears to be trying to make calls to the Mongo database when I want it to make calls to the MSSQL and I'm not really sure how to tell it what to read from. I have seen the posts advising to use the #Qualifier annotation to direct it to the correct implementation, but I don't think that that will work for my case.
#RestController
#RequestMapping("/software")
public class SoftwareEndpoint {
#Autowired
SoftwareRepository repo;
/**********************************************************************************
********************************MSSQL calls****************************************
***********************************************************************************/
#RequestMapping(value="/all",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON)
String getAllSoftware(){
System.out.println("Here1");
List<Software> allSoftware = (List<Software>) repo.findAll();
System.out.println("Here2");
//rest of method and class
Above shows a snippet of my controller class that has an instance of my SoftwareRepository. I also print to the out stream before and after the db call.
The out stream only shows "Here1", goes on to print out this line:
2016-10-04 07:35:39.810 INFO 4236 --- [nio-8080-exec-2] org.mongodb.driver.cluster : No server chosen by ReadPreferenceServerSelector{readPreference=primary} from cluster description ClusterDescription{type=UNKNOWN, connectionMode=SINGLE, all=[ServerDescription{address=localhost:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exception opening socket}, caused by {java.net.ConnectException: Connection refused: connect}}]}. Waiting for 30000 ms before timing out
and then throws an exception on timeout.
I do not have a mongo instance running locally, however there will be on where the application is being deployed, but I don't believe that this is the problem because on hitting that endpoint, it shouldn't be making a call to the Mongo database, it should be trying to reach out to MSSQL.
TLDR: How do I specify which database implementation for Spring to use for a specific repository or database call?
You can connect to different databases in spring based on the configuration in context.
The below code is for connecting to MySql and Mongo DB. You can substitute MySql with MSSQL provided you have the JDBC for it. Check http://jdbforms.sourceforge.net/UsersGuide/html/ch20s02.html for what the properties for JDBC connection mean.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="mySqldataSource" /> <!-- Change the datasource to MSSQL-->
</bean>
<bean id="mySqldataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="removeAbandoned">
<value>true</value>
</property>
<property name="removeAbandonedTimeout">
<value>30</value>
</property>
<property name="driverClassName">
<value>MSSQL_DRIVER_CLASS_NAME</value>
</property>
<property name="url">
<value>MSSQL_DATABASE_URL</value>
</property>
<property name="username">
<value>MSSQL_DB_USER_NAME</value>
</property>
<property name="password">
<value>MSSQL_DB_PASSWORD</value>
</property>
<property name="maxIdle">
<value>10</value>
</property>
<property name="maxActive">
<value>10</value>
</property>
<property name="maxWait">
<value>100000</value>
</property>
<property name="testOnBorrow">
<value>false</value>
</property>
<property name="testWhileIdle">
<value>false</value>
</property>
<property name="timeBetweenEvictionRunsMillis">
<value>60000</value>
</property>
<property name="minEvictableIdleTimeMillis">
<value>60000</value>
</property>
<property name="numTestsPerEvictionRun">
<value>1</value>
</property>
<property name="defaultTransactionIsolation" value="1" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="1" />
</bean>
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"></bean>
Below is for connecting to mongodb
<mongo:db-factory dbname="mongoDbName" host="mongoServer" port="mongoPort"/>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
<mongo:repositories base-package="com.test.repoPackage"/> <!-- Package containing the mongo repository interfaces -->
Now you can use the repositories provided by spring.
EDIT 1: Suppose name of config is springConfig.properties. In the above example for the properties dbname, host and port in mongo:db-factory, you would want the values to be configured in springConfig.properties. So lets name them below:
mongoServer = xxx.xx.xxx.xxx
mongoPort = 27017
mongoDb = testDb
Now the context file needs to be modified to import the springConfig.properties. this is done as below in the context file:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
<property name="locations" >
<list>
<value>classpath:/log4j.properties</value>
<value>classpath:/springConfig.properties</value>
</list>
</property>
</bean>
The bean mongo:db-factory would now look like:
<mongo:db-factory dbname="${mongoDb}" host="${mongoServer}" port="${mongoPort}"/>
Notice that the "keys" from config (dbname, host and port) are represented insde ${}. This will replace with values in config for the keys.
You need to have two separated config for JPA. Don't forget to disable JPA auto configuration.
#SpringBootApplication(
exclude={
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
}
)
Below is example for two different sql database. Could be easily adapted for your needs (when second datasource is mongo).
First one:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "sellitEntityManagerFactory",
transactionManagerRef = "sellitTransactionManager",
basePackages = { "co.sellit.core.api.repository.sellit" }
)
public class JpaSellitConfig {
#Bean
#ConfigurationProperties(prefix="spring.datasource.sellit")
public DataSourceProperties sellitDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties(prefix="spring.hikaricp.sellit")
public HikariConfig sellitHikariConfig() {
HikariConfig hikariConfig = new HikariConfig();
return hikariConfig;
}
#Bean
public DataSource sellitDataSource(
#Qualifier("sellitHikariConfig") HikariConfig sellitHikariConfig,
#Qualifier("sellitDataSourceProperties") DataSourceProperties sellitDataSourceProperties) {
sellitHikariConfig.setDriverClassName(sellitDataSourceProperties.getDriverClassName());
sellitHikariConfig.setJdbcUrl(sellitDataSourceProperties.getUrl());
sellitHikariConfig.setUsername(sellitDataSourceProperties.getUsername());
sellitHikariConfig.setPassword(sellitDataSourceProperties.getPassword());
sellitHikariConfig.setConnectionTestQuery("SELECT 1");
HikariDataSource hikariDataSource = new HikariDataSource(sellitHikariConfig);
return hikariDataSource;
}
#Bean
#ConfigurationProperties(prefix="spring.jpa.sellit")
public JpaVendorAdapter sellitJpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
return jpaVendorAdapter;
}
#Bean
#Autowired
public EntityManagerFactory sellitEntityManagerFactory(
#Qualifier("sellitDataSource") DataSource sellitDataSource,
#Qualifier("sellitJpaVendorAdapter") JpaVendorAdapter sellitJpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lemf = new LocalContainerEntityManagerFactoryBean();
lemf.setDataSource(sellitDataSource);
lemf.setJpaVendorAdapter(sellitJpaVendorAdapter);
lemf.setPackagesToScan("co.sellit.core.api.entity.sellit");
lemf.setPersistenceUnitName("sellitPersistenceUnit");
lemf.afterPropertiesSet();
return lemf.getObject();
}
#Bean
#Autowired
public EntityManager sellitDataSourceEntityManager(
#Qualifier("sellitEntityManagerFactory") EntityManagerFactory sellitEntityManagerFactory) {
return sellitEntityManagerFactory.createEntityManager();
}
#Bean
#Autowired
#Qualifier("sellitTransactionManager")
public PlatformTransactionManager sellitTransactionManager(
#Qualifier("sellitEntityManagerFactory") EntityManagerFactory sellitEntityManagerFactory) {
return new JpaTransactionManager(sellitEntityManagerFactory);
}
}
Second one:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "ofEntityManagerFactory",
transactionManagerRef = "ofTransactionManager",
basePackages = { "co.sellit.core.api.repository.openfire" }
)
public class JpaOpenfireConfig {
#Bean
#ConfigurationProperties(prefix="spring.datasource.openfire")
public DataSourceProperties ofDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties(prefix="spring.hikaricp.openfire")
public HikariConfig ofHikariConfig() {
HikariConfig hikariConfig = new HikariConfig();
return hikariConfig;
}
#Bean
public DataSource ofDataSource(
#Qualifier("ofHikariConfig") HikariConfig ofHikariConfig,
#Qualifier("ofDataSourceProperties") DataSourceProperties ofDataSourceProperties) {
ofHikariConfig.setDriverClassName(ofDataSourceProperties.getDriverClassName());
ofHikariConfig.setJdbcUrl(ofDataSourceProperties.getUrl());
ofHikariConfig.setUsername(ofDataSourceProperties.getUsername());
ofHikariConfig.setPassword(ofDataSourceProperties.getPassword());
ofHikariConfig.setConnectionTestQuery("SELECT 1");
HikariDataSource hikariDataSource = new HikariDataSource(ofHikariConfig);
return hikariDataSource;
}
#Bean
#ConfigurationProperties(prefix="spring.jpa.openfire")
public JpaVendorAdapter ofJpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
return jpaVendorAdapter;
}
#Bean
#Autowired
public EntityManagerFactory ofEntityManagerFactory(
#Qualifier("ofDataSource") DataSource ofDataSource,
#Qualifier("ofJpaVendorAdapter") JpaVendorAdapter ofJpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lemf = new LocalContainerEntityManagerFactoryBean();
lemf.setDataSource(ofDataSource);
lemf.setJpaVendorAdapter(ofJpaVendorAdapter);
lemf.setPackagesToScan("co.sellit.core.api.entity.openfire");
lemf.setPersistenceUnitName("ofPersistenceUnit");
lemf.afterPropertiesSet();
return lemf.getObject();
}
#Bean
#Autowired
public EntityManager ofDataSourceEntityManager(
#Qualifier("ofEntityManagerFactory") EntityManagerFactory ofEntityManagerFactory) {
return ofEntityManagerFactory.createEntityManager();
}
#Bean
#Autowired
#Qualifier("ofTransactionManager")
public PlatformTransactionManager ofTransactionManager(
#Qualifier("ofEntityManagerFactory") EntityManagerFactory ofEntityManagerFactory) {
return new JpaTransactionManager(ofEntityManagerFactory);
}
}
So repositories from package co.sellit.core.api.repository.sellit will use sellit db
But repositories from package co.sellit.core.api.repository.openfire will use openfire database.
UPDATE (xml config version)
<bean id="openfireDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/openfire?characterEncoding=UTF-8" />
</bean>
<bean id="sellitDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/sellit?characterEncoding=UTF-8" />
</bean>
<bean id="openfireSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="openfireDataSource" />
<property name="packagesToScan" value="co.sellit.core.api.repository.openfire" />
<property name="hibernateProperties">
<props>
...
</props>
</property>
</bean>
<bean id="sellitSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="sellitDataSource" />
<property name="packagesToScan" value="co.sellit.core.api.repository.sellit" />
<property name="hibernateProperties">
<props>
...
</props>
</property>
</bean>
<bean id="openfireTxnManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="openfireSessionFactory" />
</bean>
<bean id="sellitTxnManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sellitSessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="openfireTxnManager" />
<tx:annotation-driven transaction-manager="sellitTxnManager" />
I am new to Spring batch and I am trying to use Spring Batch with restartable feature. I am using MapJobRepositoryFactoryBean as Job repository. Everything looks fine. But when I run the same job multiple times I could see the execution time increasing considerably. I guess some memory leak is happening.If no job is running, I am cleaning the repository as well. But no luck. How do I know whats happening exactly. After running the same job for 4-5 times, the execution time is going to 2-3 times of the first execution.
<jpa:repositories base-package=".reference.data.repository"/>
<context:annotation-config />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
<property name="url"
value="jdbc:sqlserver://${reference-data-manager.database.hostname}:
${reference-data-manager.database.port};
database=${reference-data-manager.database.name};
user=${reference-data-manager.database.username};
password=${reference-data-manager.database.password}" />
</bean>
<bean id="simpleJobConfiguration" class="reference.job.SimpleJobConfiguration">
</bean>
<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>
<bean id="importJob" class="org.springframework.batch.core.job.SimpleJob" scope="prototype">
<property name="jobRepository" ref="jobRepository"></property>
</bean>
<batch:step id="importCodesStep">
<batch:tasklet allow-start-if-complete="true">
<batch:chunk reader="codeMappingReader" writer="codeMappingWriter"
processor="codeMappingProcessor" commit-interval="${reference-data-manager.batch.size}"
skip-policy="reasonRemarkAssnSkipPolicy" skip-limit="${reference-data-manager.skip.limit}">
<batch:skippable-exception-classes>
<batch:include class="org.springframework.batch.item.ParseException"/>
</batch:skippable-exception-classes>
</batch:chunk>
</batch:tasklet>
<batch:listeners>
<batch:listener ref="reasonRemarkAssnStepListener"/>
<batch:listener ref="reasonRemarkAssnSkipListener"/>
</batch:listeners>
</batch:step>
<bean id="reasonRemarkAssnStepListener" class="reference.listeners.ReasonRemarkAssnStepListener">
</bean>
<bean id="reasonRemarkAssnSkipListener" class="reference.listeners.ReasonRemarkAssnSkipListener">
</bean>
<bean id="reasonRemarkAssnSkipPolicy" class="reference.listeners.ReasonRemarkAssnSkipPolicy">
<!-- <property name="skipLimit" value="5"/> -->
</bean>
<bean id="codeMappingWriter" class="reference.writer.ReasonRemarkAssnWriter" scope="step">
<property name="entityManagerFactory" ref="emf" />
</bean>
<bean id="codeMappingProcessor" class="reference.processors.ReasonRemarkAssnProcessor" scope="step">
<property name="userId" value="#{jobParameters['USER_ID']}" />
<property name="clientMnemonic" value="#{jobParameters['CLIENT_MENOMONIC']}" />
</bean>
<bean id="codeMappingReader" class="reference.readers.ReasonRemarkAssnReader" scope="step">
<property name="org" value="#{jobParameters['ORG']}"/>
<property name="resource" value="" />
<property name="linesToSkip" value="1" />
<property name="lineMapper">
<bean class="reference.mapper.ReasonRemarkAassnLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="Reason Code,Remark Code,Category,Category Priority,Ignore Insight Processing,Active,Comment" />
</bean>
</property>
<property name="fieldSetMapper" ref="reasonRemarkAssnMapper"/>
</bean>
</property>
</bean>
<bean id="reasonRemarkAssnMapper" class="reference.mapper.ReasonRemarkAssnMapper">
<property name="codeGroups" value="${reference-data-manager.code.groups}"></property>
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
</bean>
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean class="CryptoPropertyPlaceholderConfigurer">
<property name="configUtil" ref="configUtil" />
<property name="location"
value="file:config/reference-data-manager.conf" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<bean name="configUtil" class="CoreConfigUtil">
<property name="crypto" ref="crypto" />
</bean>
<bean id="referenceDataManager" class="reference.service.impl.ReferenceDataManagerImpl">
<property name="step" ref="importCodesStep"></property>
</bean>
Here is my job invocation...
#RestfulServiceAddress("/reference")
public class ReferenceDataManagerImpl implements ReferenceDataManager {
#Autowired
private JobLauncher jobLauncher;
#Autowired
IDataAccessService dataAccessService;
#Autowired
private IConfigurationServiceFactory configurationServiceFactory;
#Autowired
private SimpleJob job;
#Autowired
private TaskletStep step;
#Autowired
private SimpleJobConfiguration jobConfiguration;
#Autowired
private MapJobRepositoryFactoryBean jobRepository;
#Override
public Response importData(MultipartBody input, String org,
String userId, MessageContext mc) throws ServiceFault {
Response.ResponseBuilder builder = null;
ReasonRemarkAssnResponse responseObj = null;
boolean isSuccess = false;
UserInfo userInfo= dataAccessService.userInfoFindByUserId(userId);
JobParametersBuilder jobParametersBuilder = new JobParametersBuilder();
List<Attachment> attachments = input.getAllAttachments();
DataHandler dataHandler = attachments.get(0).getDataHandler();
byte[] bFile = null;
if(null != dataHandler){
try {
InputStream is = dataHandler.getInputStream();
bFile = new byte[is.available()];
is.read(bFile);
is.close();
} catch (IOException e) {
//TODO
}
}
SimpleJobConfiguration.customStorage.put(org, bFile);
jobParametersBuilder.addLong(ReferenceConstants.JOB_PARAMS_USERID, userInfo.getId());
jobParametersBuilder.addString(ReferenceConstants.JOB_PARAMS_ORG, org);
jobParametersBuilder.addString(ReferenceConstants.JOB_PARAMS_TIME, Calendar.getInstance().getTime().toString());
String response = "{error:Error occured while importing the file}";
job.setName(ReferenceConstants.JOB_PARAMS_JOB_NAME_PREFIX+org.toUpperCase());
job.addStep(step);
job.setRestartable(true);
JobExecution execution = null;
try {
execution = jobLauncher.run(job, jobParametersBuilder.toJobParameters());
isSuccess = true;
} catch (JobExecutionAlreadyRunningException e) {
//TODO
}catch (JobRestartException e) {
//TODO
}catch (JobInstanceAlreadyCompleteException e) {
//TODO
}catch (JobParametersInvalidException e) {
//TODO
}
response = prepareResponse(responseObj);
synchronized (SimpleJobConfiguration.customStorage){
if(null != SimpleJobConfiguration.customStorage.get(org)){
SimpleJobConfiguration.customStorage.remove(org);
}
if(SimpleJobConfiguration.customStorage.isEmpty()){
jobRepository.clear();
}
}
builder = Response.status(Response.Status.OK);
builder.type(MediaType.APPLICATION_JSON);
builder.entity(response);
return builder.build();
}
}
Don't use the MapJobRepositoryFactoryBean unless it's for testing. That's all it's intended for.
You shouldn't be building a new Job instance with every call to the controller. If you have stateful components within a step, declare them as step scoped so that you get a new instance per step execution.
The Map based JobRepository keeps everything in memory. That's the idea. So as you execute more and more jobs, the repository is going to grow...eating up your space in memory.
For launching a job in a controller, give something like this a try:
#RestController
public class JobLaunchingController {
#Autowired
private JobLauncher jobLauncher;
#Autowired
private Job job;
#RequestMapping(value = "/", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.ACCEPTED)
public void launch(#RequestParam("name") String name) throws Exception {
JobParameters jobParameters =
new JobParametersBuilder()
.addString("name", name)
.toJobParameters();
this.jobLauncher.run(job, jobParameters);
}
}
When use spring data and jdbctemplate in same function
spring data work fine but jdbc update not work .
this is service class.
jdbc update then not error occurred but database not updated
#Service
public class SampleService {
private static final Logger logger = LoggerFactory.getLogger(SampleService.class);
#Inject
private SampleRepository sampleRepository;
#Inject
private JpaTransactionManager transactionManager;
private JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(transactionManager.getDataSource());
}
#Transactional(propagation=Propagation.REQUIRED)
public void execute(){
sampleRepository.deleteAll();
sampleRepository.save(new Sample("sample"));
logger.info("data count (after jpa insert) : {}", sampleRepository.count());//1
int count = jdbcTemplate().update("insert into Sample (sample) values ( ? )", "sample");
logger.info("jdbc insert count: {}", count);//1
logger.info("data count (after jdbc insert) : {}", sampleRepository.count());//expected: 2 , actual:1
count = jdbcTemplate().queryForObject("select count(id) from Sample", Integer.class);
logger.info("data count (after jdbc insert) : {}", count); //jdbc count query result also return 1
}
when not use #Transactional annotation then work fine.
public void execute2(){
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("testTx");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);
... this method work fine ...
transactionManager.commit(status);
}
}
and this is data access configuration.
<context:annotation-config />
<tx:jta-transaction-manager />
<tx:annotation-driven transaction-manager="transactionManager" />
<jpa:repositories base-package="example" />
<context:property-placeholder location="classpath:config.properties" />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="DefaultUnit" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="${db.showsql}" />
<property name="generateDdl" value="${db.generateDDL}" />
<property name="database" value="${db.database}" /><!-- H2 -->
</bean>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="basicDataSource" />
</bean>
<bean id="basicDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.user}" />
<property name="password" value="${db.pass}" />
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
My acitvemq server always print error below :
2014-07-12 16:14:27,820 | ERROR | Could not accept connection :
org.apache.activemq.transport.tcp.ExceededMaximumConnectionsException:
Exceeded the maximum number of allowed client connections.
See the 'maximumConnections' property on the TCP transport configuration URI
in the ActiveMQ configuration file (e.g., activemq.xml)
| org.apache.activemq.broker.TransportConnector
| ActiveMQ Transport Server Thread Handler:
tcp://0.0.0.0:61616?maximumConnections=1000&wireformat.maxFrameSize=104857600
When I restart the server it will be ok. But after a few days the error come out again.
I don't why the connections always increase to 1000.
My server config:
<!-- activeMQ -->
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${jms.brokerURL}"></property>
</bean>
<!-- Spring Caching -->
<bean id="cachingConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="jmsConnectionFactory" />
<property name="sessionCacheSize" value="10" />
</bean>
<!-- Spring JMS Template -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="cachingConnectionFactory" />
<property name="explicitQosEnabled" value="true" />
<property name="priority" value="4" />
</bean>
<bean id="scoreQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="SCORE" />
</bean>
<bean id="scoreMessage" class="com.tt.score.mq.server.ScoreMessage"></bean>
<bean id="scoreListener"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsConnectionFactory"></property>
<property name="destination" ref="scoreQueue"></property>
<property name="messageListener" ref="scoreMessage"></property>
<property name="concurrentConsumers" value="10" />
<property name="maxConcurrentConsumers" value="100" />
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
</bean>
My client config xml:
<!-- Spring Caching -->
<bean id="cachingConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="jmsConnectionFactory" />
<property name="sessionCacheSize" value="10" />
</bean>
<!-- Spring JMS Template -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="cachingConnectionFactory" />
<property name="explicitQosEnabled" value="true" />
<property name="priority" value="4" />
</bean>
<bean id="messageProducer" class="com.tt.score.mq.client.MessageProducer">
<property name="jmsTemplate" ref="jmsTemplate" />
<property name="scoreQueue" ref="scoreQueue" />
</bean>
<bean id="scoreQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="SCORE" />
</bean>
Other info:
acitvemq server : 5.8.0
client acitvemq : 5.4.2
spring : 3.0.7
spring-jms : 3.0.7
We use transactionManager so the DefaultMessageListenerContainer's cachelevel will be set to none.
---update add dao config----------------------------------
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${jdbc.driverClass}</value></property>
<property name="username"><value>${jdbc.user}</value></property>
<property name="url"><value>${jdbc.jdbcUrl}</value></property>
<property name="password">
<bean class="com.tongbanjie.commons.util.EncryptDBPasswordFactory">
<property name="password" value="${jdbc.password}" />
</bean>
</property>
<property name="maxActive"><value>${jdbc.maxActive}</value></property>
<property name="initialSize"><value>${jdbc.initialSize}</value></property>
<property name="maxWait"><value>60000</value></property>
<property name="maxIdle"><value>${jdbc.maxIdle}</value></property>
<property name="minIdle"><value>5</value></property>
<property name="removeAbandoned"><value>true</value></property>
<property name="removeAbandonedTimeout"><value>180</value></property>
<property name="timeBetweenEvictionRunsMillis"><value>60000</value></property>
<property name="minEvictableIdleTimeMillis"><value>1800000</value></property>
<property name="defaultAutoCommit" value="false" />
<property name="connectionProperties">
<value>bigStringTryClob=true;clientEncoding=UTF-8;defaultRowPrefetch=50;serverEncoding=ISO-8859-1</value>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!-- myBatis -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:META-INF/mybatis/score-configuration.xml" />
<property name="mapperLocations" value="classpath*:META-INF/mybatis/mapper/*.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="commonSqlSessionDao" abstract="true">
<property name="sqlSessionFactory">
<ref bean="sqlSessionFactory" />
</property>
</bean>
-----post the code that we how to use the template now
the jmsTemplate wrapped in a class
public class MessageProducer {
private JmsTemplate jmsTemplate;
private ActiveMQQueue scoreQueue;
public void sendScoreQueue(Map<String, String> userMap) {
sendMessage(this.scoreQueue, userMap);
}
private void sendMessage(Destination destination, final Map<String, String> map) {
this.jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
MapMessage message = session.createMapMessage();
for (String key : map.keySet()) {
message.setStringProperty(key, (String) map.get(key));
}
return message;
}
});
}
}
And we use a thead to send call the MessageProducer class's sendScoreQueue method.
As follows:
//the code is old and ugly.that is the original position we call the mq.
ThreadUtils.execute(new Thread(new SendMsgThread(dycc, ScoreMQSendType.SEND_TYPE_SCORE)));
///
public class ThreadUtils {
protected static ThreadPoolExecutor executor = null;
public static Properties Props = null;
public static void execute(Thread thread) {
executor.execute(thread);
}
static {
if (executor == null)
Integer corePoolSize = 5;
Integer maximumPoolSize = 10;
Integer keepAliveTime = 3000;
executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MINUTES,
new LinkedBlockingQueue());
}
}
public class SendMsgThread implements Runnable {
private Log log = LogFactory.getLog(SendMsgThread.class);
private Map<String, String> map;
private String type;
private static MessageProducer producer = null;
public SendMsgThread(Map<String, String> map, String type){
this.type = type;
this.map = map;
}
public void run() {
try {
if(type.equals(ScoreMQSendType.SEND_TYPE_SCORE) || type.equals(ScoreMQSendType.SEND_TYPE_REGISTER)) {
producer.sendMessage(map);
}
} catch (Exception e) {
this.log.error("sendMsgThread sendScoreQueue error.", e);
}
}
static {
if (producer == null) producer =
(MessageProducer )SpringContextHolder.getBean(MessageProducer .class);
}
}
For this scenario you should use PooledConnectionFactory instead of cachingConnectionFactory.
More information can be found here.The difference between them can be found here
I had the same problem recurring with same error, and the solution was obtained as a result of trial and error, use jms call to concurrent consumers max to 101, instead of 100, and see if results repeat, adding more to the value results the code executed further on debugger, you will reach a value when code works , Also the solution appears to be using a connection pool factory.
Try this, hope it works, rest my implementation is same as yours in bean file
I am trying to inject from my context.xml file a datasource. I have this config file but the issue is, i have more than one datasource at anytime. When i try to inject any of them, I get an error that says there is a no matching bean.
Here is my code.
#Inject
#Named("dataSourceAccounts")
//#Autowired
#Override
public void setDataSource(DataSource dataSource) {
// TODO Auto-generated method stub
this.jdbcTemplate = new JdbcTemplate(dataSource);
System.out.println("jdbcTemplate is null? " + (jdbcTemplate == null));
}
//for the context.xml
<context:component-scan base-package="business" />
<context:component-scan base-package="middleware" />
<context:component-scan base-package="presentation" />
<context:annotation-config/>
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
<bean id="txManagerProducts" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceProducts"/>
</bean >
<bean id="txManagerAccounts" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceAccounts"/>
</bean >
<bean id="dataSourceProducts" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/productsdb" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/accountsdb" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
Thanks for your help in a advance.
If you want to specify the qualified name for the dependency that should be injected, the #Named should precede the param definition:
#Inject
#Override
public void setDataSource(#Named("dataSourceAccounts") DataSource dataSource) {
// TODO Auto-generated method stub
this.jdbcTemplate = new JdbcTemplate(dataSource);
System.out.println("jdbcTemplate is null? " + (jdbcTemplate == null));
}
or using spring annotations:
#Autowired
#Override
public void setDataSource(#Qualifier("dataSourceAccounts") DataSource dataSource)
this should work
#javax.annotation.Resource(name="dataSourceProducts")
public void setDataSource(DataSource dataSource) {
...
}
javax.annotation.Resource is in Java SE