I am working on Spring MVC web application which is using JDBC Quartz Scheduler to execute jobs. This web application has multiple services each of which is packaged as an jar and has associated jobs. I have one quartz configuration which is in web project, under which I have created beans for jobs, triggers and scheduler. Under scheduler configuration, i specify a list of triggers. This was good when we started our project. As we started adding features, number of jobs increased and its now around 100+. It has become difficult to maintain this file. Is there any approach available make it modular so that the jobs/trigger/associating trigger to scheduler are not defined in the one single file.
<bean name="incidentAutoClosure" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="itsm.scheduler.task.IncidentAutoClosureTask"/>
</bean>
<bean id="incidentAutoClosureTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="incidentAutoClosure" />
<property name="cronExpression" value="0 0 12 1/1 * ? *" />
</bean>
<bean id="quartzJdbcScheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
p:waitForJobsToCompleteOnShutdown="true">
<property name="autoStartup" value="true" />
<property name="startupDelay" value="0" />
<property name="overwriteExistingJobs" value="false" />
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">mvcJdbcScheduler</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.PostgreSQLDelegate</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">${quartz.async.threadCount}</prop>
<prop key="org.quartz.threadPool.threadPriority">${quartz.async.threadPriority}</prop>
<prop key="org.quartz.jobStore.misfireThreshold">${quartz.async.misfireThreshold}</prop>
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer">true</prop>
<prop key="org.quartz.plugin.jobHistory.class">org.quartz.plugins.history.LoggingJobHistoryPlugin</prop>
<prop key="org.quartz.plugin.jobHistory.jobToBeFiredMessage">${quartz.async.initTriggerFormat}</prop>
<prop key="org.quartz.plugin.jobHistory.jobSuccessMessage">${quartz.async.endTriggerFormat}</prop>
</props>
</property>
<property name="triggers">
<list>
<ref bean="incidentAutoClosureTrigger" />
<!-- List of 100 Jobs -->
</list>
</property>
</bean>
Few things, Are you using spring boot? A good approach to this problem would be to create auto configuration class where you can have a list of combinations. Here is a sample:
#Configuration
#ConditionalOnBean(SomeClassDependency.class)
public class JobConfig {
#Bean
#ConditionalOnProperty(....)
public SchedulerFactoryBean config1() {
....
}
#Bean
#ConditionalOnProperty(....)
public SchedulerFactoryBean config2() {
....
}
#Bean
#ConditionalOnProperty(....)
public SchedulerFactoryBean config3() {
....
}
}
public class MyDependency {
private final SchedulerFactoryBean schedulerFactoryBean;
public MyDependency(#Qualifier("config2") SchedulerFactoryBean schedulerFactoryBean) {
this.schedulerFactoryBean = schedulerFactoryBean
}
}
you can do it programmatically through a configuration class that will automatically configure your bean instead of using an xml file;
here is what I used. Note: having the applicationContext added to the jobFactory allows Quartz schedule jobs to be aware of spring autowired classes when the job executes otherwise you'll get a null autowire
#Configuration
public class PortalQuartzSchedulerConfiguration {
#Autowired
private ApplicationContext applicationContext;
#Bean
public JobFactory jobFactory() {
ApplicationContextHolder jobFactory = new ApplicationContextHolder();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
#Bean
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(true);
factory.setSchedulerName("Portal scheduler");
factory.setOverwriteExistingJobs(true);
factory.setJobFactory(jobFactory());
return factory;
}
}
Related
We are currently migrating from xml configuration to complete annotation with java configuration based Spring application. With the annotation approach #Transactional we can achieve but the we need to write for each and every method.
In XML we configured (OLD).
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="delete*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
<prop key="update*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
<prop key="save*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
<prop key="get*">PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITTED,readOnly</prop>
<prop key="is*">PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITTED,readOnly</prop>
<!--<prop key="*">PROPAGATION_REQUIRED</prop> -->
</props>
</property>
</bean>
transactionManager is org.springframework.orm.hibernate3.HibernateTransactionManager
<bean id="xxxxSVC" parent="txProxyTemplate">
<property name="target">
<bean class="XXX.XXX.XXX.SVCImpl">
<property name="xxxxDao" ref="xxxDao"></property>
</bean>
</property>
</bean>
txProxyTemplate is parent class of each service class.
So, please suggest how to configure similar code in java configuration. Thanks for your valuable time spent and support us.
#Barath Comment
Bean
#Bean
public TransactionProxyFactoryBean setTransactionProperties() throws IOException {
TransactionProxyFactoryBean transactionProxyFactoryBean = new TransactionProxyFactoryBean();
transactionProxyFactoryBean.setTransactionManager(transactionManager(sessionFactory()));
Properties transactionAttributesProps = new Properties();
transactionAttributesProps.setProperty("delete*", "PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED");
transactionAttributesProps.setProperty("update*", "PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED");
transactionAttributesProps.setProperty("save*", "PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED");
transactionAttributesProps.setProperty("get*", "PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITTED,readOnly");
transactionAttributesProps.setProperty("is*", "PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITTED,readOnly");
transactionProxyFactoryBean.setTransactionAttributes(transactionAttributesProps);
transactionProxyFactoryBean.afterPropertiesSet();
return transactionProxyFactoryBean;
}
How to configure with each service implementation class, we can use it for single as a service layer may contain N classes. There is a method setTarget(Object target). Now how can we configure all the N classes. Please sugggest how can we configure.
A sample configuration for this case :
#Bean
public TransactionProxyFactoryBean txProxyTemplate(){
TransactionProxyFactoryBean txFactory=new TransactionProxyFactoryBean();
txFactory.setTransactionManager(new JpaTransactionManager()); // any transcation manager
txFactory.setTransactionAttributes(properties());
return txFactory;
}
#Bean
Properties properties(){
Properties properties=new Properties();
properties.put("delete*", "PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED");
//set al lthe properties
return properties;
}
#Bean
public TransactionProxyFactoryBean xxxxSVC(TransactionProxyFactoryBean txFactory){
txFactory.setTarget(testEntity());
return txFactory;
}
#Bean
TestEntity testEntity(){
return new TestEntity();
}
I am using the Spring configuration to test Spring-Hibernate Transactions.
<beans ...>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- hibernate 4 onwards annotationsessionfactorybean is replaced with localsessionfactory bean -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.fg.arch.test.transaction.Foo</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<!-- <prop key="hibernate.current_session_context_class">org.hibernate.context.internal.ThreadLocalSessionContext</prop> -->
</props>
</property>
</bean>
</beans>
My service layer is annotated with #Transactional.
This is my DAO:
public class FooHibernateDaoImpl implements FooDao {
private SessionFactory sessionFactory;
public void testFoo(Foo foo) throws Throwable {
System.out.println(" --- ");
sessionFactory.openSession().save(foo);
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
Explicitly opening the session using the openSession() method does not cause a problem however when I change to getCurrentSession() I am getting an exception.
I have two questions.
Is it good practice to call openSession() in every DAO method.
How can I make getCurrentSession() work so that it will not give me an exception like no active transaction present ?
Thanks.
To answer your questions:
No, its not. The #Transactional annotation that should be on your service class method calling testFoo() is opening the session for you. You should use getCurrentSession() in the DAO to get this session.
You can, but you shouldn't. That's the entire point of using the Hibernate SessionFactory with annotation based transaction management. As long as you are marking your service methods transactional, you shouldn't have a problem.
As a side note, why are you not Autowiring your SessionFactory? Don't use setters to set something that should be Autowired. Otherwise you may as well not use Spring.
We have a scenario where in the catalog/schema combination is different for the entity classes inside certain package from the default one used by all others. I am trying to set Catalog and Schema on #Table annotation using PersistenceUnitPostProcessors callback at runtime using javaassist as below.
The issue: The added member values on javaassist annotation are NOT getting reflected on to the actual class associated with it. Please help me in finding the wrong lines of code; OR if there are other ways to achieve this, more than happy to know.
Note: I do not want to create a separate EntityManagerFactory for each catalog/schema combination - it is not really required in our case as the datasource is same.
related content in spring context :
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven />
<bean name="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="mainUnit" />
<property name="packagesToScan" value="com.mycompany.lob.domain" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="persistenceUnitPostProcessors">
<list>
<bean class="com.mycompany.lob.jpa.CustomPersistenceUnitPostProcessor"/>
</list>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SqlmxDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.jdbc.batch_size">100</prop>
<prop key="hibernate.order_inserts">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.connection.autocommit">true</prop>
<prop key="hibernate.default_schema">DEFAULT_SCHEMA</prop>
<prop key="hibernate.default_catalog">DEFAULT_CATALOG</prop>
</props>
</property>
</bean>
PersistenceUnitPostProcessors callback :
public class CustomPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
#Value("${user.schema}")
private String userSchema;
#Value("${user.catalog}")
private String userCatalog;
private static final Logger LOGGER = LoggerFactory.getLogger(CustomPersistenceUnitPostProcessor.class);
#SuppressWarnings("unchecked")
#Override
public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
LOGGER.info("MutablePersistenceUnitInfo : {} ",pui);
List<String> jpadomains = pui.getManagedClassNames();
for (Iterator<?> iterator = jpadomains.iterator(); iterator.hasNext();) {
String clazzName = (String) iterator.next();
if(clazzName.startsWith("com.mycompany.lob.domain.user")){
try {
//modify annotation attributes using JavaAssist
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get(clazzName);
ClassFile classFile = ctClass.getClassFile();
ConstPool constPool = classFile.getConstPool();
AnnotationsAttribute annotationsAttribute = (AnnotationsAttribute)classFile.getAttribute(AnnotationsAttribute.visibleTag);
if(annotationsAttribute!=null){
//Get hold of #Table annotation
Annotation tableAnnotation = annotationsAttribute.getAnnotation("javax.persistence.Table");
if(tableAnnotation!=null){
tableAnnotation.addMemberValue("catalog", new StringMemberValue(userCatalog, constPool));
tableAnnotation.addMemberValue("schema", new StringMemberValue(userSchema, constPool));
annotationsAttribute.addAnnotation(tableAnnotation);
LOGGER.debug("Schema-Table : {} - {} ", ((StringMemberValue)tableAnnotation.getMemberValue("schema")).getValue(),
((StringMemberValue)tableAnnotation.getMemberValue("name")).getValue() );
//write the file back
ctClass.writeFile();
}
}
} catch (Exception e) {
LOGGER.error("Schema/Catalog could not be altered for {} ",clazzName);
}
}
}
}
}
Simple answer:
19. Multitenancy
Complex catalog mapping:
interface PhysicalNamingStrategy in Hibernate v5 is helpful.
public interface PhysicalNamingStrategy {
public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment);
public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment);
....
}
Check the Example 2. Example PhysicalNamingStrategy implementation in Hibernate 5 User Guide and how to config it
I have a series of integration tests using Spring tests and a H2 memory database. I've noticed that the h2 database does not get rolled back after every test.
This is my config:
#RunWith(SpringJUnit4ClassRunner.class)
public class BlaServiceIntegrationTest extends AbstractTest {
#Autowired
private BlaService blaService;
#Test()
public void doBla() {
// some other test that writes in the database
}
#Test()
public void saveNewBla() {
assertEquals(0, findAllBlas().size()); // <-- fails, the db is not empty.
// the previous test did not roll back
}
Then there is the AbstractTest:
#ContextConfiguration(locations = { "classpath:test-with-db.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional()
public class AbstractTest {
private static final Logger LOG = LoggerFactory.getLogger(AbstractTest.class);
#Autowired
protected SessionFactory sessionFactory;
public AbstractTest() {
super();
}
#SuppressWarnings("unchecked")
protected List<Bla> findAllBlas() {
return sessionFactory.getCurrentSession().createCriteria(Bla.class).list();
}
The Spring config:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource "
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}"
p:username="${jdbc.username}" p:password="${jdbc.password}">
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl}</prop>
<!-- <prop key="hibernate.use_sql_comments">false</prop> -->
</props>
</property>
<property name="packagesToScan" value="com.company.bla.back.domain"></property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory">
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="hibernateConfiguration" factory-bean="&sessionFactory"
factory-method="getConfiguration" />
And the config.properties specific to the test:
################### MySQL JDBC Configuration ##################
jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:mem;DB_CLOSE_ON_EXIT=FALSE
jdbc.username=
jdbc.password=
################### Hibernate Configuration ##########################
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.show_sql=false
hibernate.hbm2ddl=create-drop
hibernate.generate_statistics=false
And the service being tested:
#Service("blaService")
#Transactional(propagation = Propagation.MANDATORY)
public class BlaServiceImpl implements BlaService {
Never mind, I found the solution myself. It wasn't a rollback problem at all?
There was a mistake in my findAllBla() method? It needed a:
.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
i have this problem: "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here". I'm using spring + hibernate 3. I already had checking this in other questions, but they donĀ“t solve my problem. Here, my applicationContext of Spring.
`<bean id="ServGenerico" class="servico.AbsServicoGenerico" />
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${jdbc.driver}" p:jdbcUrl="${jdbc.url}" p:user="${jdbc.username}"
p:password="${jdbc.password}" p:max PoolSize="10" p:minPoolSize="5" p:maxStatements="0" destroy-method="close">
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<context:component-scan base-package="dao" />
<context:component-scan base-package="dao.impl" />
<context:component-scan base-package="servico" />
<bean id="grupoDeAcaoDao" class="dao.impl.GrupoDeAcaoDao" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="dados" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
<prop key="hibernate.show_sql">true</prop>
<!-- <prop key="hibernate.current_session_context_class">org.hibernate.context.internal.ThreadLocalSessionContext</prop>
--><!-- <prop key="hibernate.c3p0.max_size">10</prop>
<prop key="hibernate.c3p0.min_size">2</prop>
<prop key="hibernate.c3p0.timeout">5000</prop>
<prop key="hibernate.c3p0.max_statements">10</prop>
<prop key="hibernate.c3p0.idle_test_period">3000</prop>
<prop key="hibernate.c3p0.acquire_increment">2</prop> -->
<!-- <prop key="hibernate.transaction.jta.platform">
org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform
</prop> -->
</props>
</property>
</bean>`
My service
#Service
#Transactional
public class ServGrupoDeAcao extends AbsServicoGenerico<GrupoDeAcao>
{
public ServGrupoDeAcao()
{
super();
setDao(new GrupoDeAcaoDao());
}
public ServGrupoDeAcao(Class<?> clazz)
{
super(clazz);
}
/*#Autowired
#Qualifier("grupoDeAcaoDao")
public void setDao(GrupoDeAcaoDao dao)
{
super.setDao(dao);
}*/
}
And my DAO
#Repository
#Transactional
public class GrupoDeAcaoDao extends AbsDao<GrupoDeAcao, Long>
{
#Override
public boolean equals(Object object)
{
// TODO Auto-generated method stub
return false;
}
#Override
public int hashCode()
{
// TODO Auto-generated method stub
return 0;
}
#Override
public Object executarConsultaPersonalizada(NomeMetodo nome,
Parametros parametros)
{
// TODO Auto-generated method stub
return null;
}
public boolean daoCompativel(Class<?> clazz)
{
return GrupoDeAcao.class.equals(clazz);
}
The class AbsDao is a generic class that have in your methods, save, update e etc.
And this class have the #Repository and #Transactional Anotations