Spring 4 + hibernate 4 + c3p0 : Could not obtain transaction-synchronized Session for current thread - spring

I an trying to run simple application that uses Spring 4, Hibernate 4, and c3p0 pooling, and I keep getting this error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDao': Invocation of init method failed; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1560)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:540)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4937)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
Caused by: org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at ggdb.components.database.dao.GenericDao.getCurrentSession(GenericDao.java:61)
at ggdb.components.database.dao.UserDao.getUser(UserDao.java:36)
at ggdb.components.database.dao.UserDao.postConstruct(UserDao.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:349)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:300)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133)
... 23 more
Here is my configuration:
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:application.properties")
public class DatabaseConfigutarion implements TransactionManagementConfigurer {
#Autowired
private Environment env;
public class PropertiesKeys {
public static final String DATABASE_HOST = "database.host";
public static final String DATABASE_PORT = "database.port";
public static final String DATABASE_USERNAME = "database.username";
public static final String DATABASE_PASSWORD = "database.password";
public static final String DATABASE_NAME = "database.database_name";
public static final String DATABASE_ADDITIONAL_CONNECTION_PROPERTIES = "database.additional.connection.properties";
public static final String HIBERNATE_DEFAULT_SCHEMA = "hibernate.default_schema";
public static final String HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
}
#Bean
public SessionFactory sessionFactory() {
String databaseUrl = String.format("jdbc:mysql://%s:%s/%s", env.getProperty(PropertiesKeys.DATABASE_HOST), env.getProperty(PropertiesKeys.DATABASE_PORT), env.getProperty(PropertiesKeys.DATABASE_NAME));
String additionnalProperties = env.getProperty(PropertiesKeys.DATABASE_ADDITIONAL_CONNECTION_PROPERTIES);
databaseUrl = ( additionnalProperties.isEmpty() ? databaseUrl : String.format("%s?%s", databaseUrl, additionnalProperties) );
org.hibernate.cfg.Configuration configuration = new org.hibernate.cfg.Configuration();
configuration.setProperty("hibernate.connection.driver_class", com.mysql.jdbc.Driver.class.getName());
configuration.setProperty("hibernate.connection.url", databaseUrl);
configuration.setProperty("hibernate.connection.username", env.getProperty(PropertiesKeys.DATABASE_USERNAME));
configuration.setProperty("hibernate.connection.password", env.getProperty(PropertiesKeys.DATABASE_PASSWORD));
configuration.setProperty("hibernate.c3p0.min_size", "5");
configuration.setProperty("hibernate.c3p0.max_size", "20");
configuration.setProperty("hibernate.c3p0.timeout", "1800");
configuration.setProperty("hibernate.c3p0.max_statements", "50");
configuration.setProperty("hibernate.current_session_context_class", org.springframework.orm.hibernate4.SpringSessionContext.class.getName());
configuration.setProperty("hibernate.dialect", org.hibernate.dialect.MySQL5InnoDBDialect.class.getName());
configuration.setProperty("hibernate.show_sql", "false");
configuration.setProperty("hibernate.format_sql", "false");
configuration.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");
configuration.setProperty("hibernate.default_schema", env.getProperty(PropertiesKeys.HIBERNATE_DEFAULT_SCHEMA));
configuration.setProperty("hibernate.hbm2ddl.auto", env.getProperty(PropertiesKeys.HIBERNATE_HBM2DDL_AUTO));
configuration.addAnnotatedClass(ggdb.components.database.entities.User.class);
configuration.addAnnotatedClass(ggdb.components.database.entities.Role.class);
ServiceRegistry serviceRegistry = (new StandardServiceRegistryBuilder())
.applySettings(configuration.getProperties())
.build();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
#Bean
public HibernateTransactionManager transactionManager(){
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory());
return transactionManager;
}
#Override
public HibernateTransactionManager annotationDrivenTransactionManager() {
return transactionManager();
}
public Environment getEnv() {
return env;
}
public void setEnv(Environment env) {
this.env = env;
}
}
And code that throws error:
#Repository
public class UserDao extends GenericDao<User> {
public UserDao() {
super(User.class);
}
// TODO: this method is temporary,
// should be removed later,
#PostConstruct
#Transactional
public void postConstruct() {
User user = getUser("user");
if (user == null) {
user = new User();
user.setUsername("user");
user.setPassword("pass");
save(user);
}
}
#Transactional
public User getUser(String username) {
#SuppressWarnings("unchecked")
List<User> users = (List<User>) getCurrentSession().getNamedQuery(User.FIND_BY_USERNAME).setParameter("username", username).list();
return (users.isEmpty() ? null : users.get(0));
}
}
Extended class code:
public class GenericDao<T> {
private final Class<T> parameterClass;
#Autowired
protected SessionFactory sessionFactory;
public GenericDao(Class<T> parameterClass) {
this.parameterClass = parameterClass;
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
public Long save(T t) {
return (Long) getSession().save(t);
}
public void delete(Long id) {
getSession().delete(id);
}
#SuppressWarnings("unchecked")
public T get(Long id) {
return (T) getSession().get(parameterClass, id);
}
public void refresh(T t) {
getSession().refresh(t);
}
#SuppressWarnings("unchecked")
public T merge(T t) {
return (T) getSession().merge(t);
}
public void update(T t) {
getSession().update(t);
}
public void saveOrUpdate(T t) {
getSession().saveOrUpdate(t);
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
Can you help me? Where is the problem?

Invocation of init method failed. You could try to add this:
#Autowired
public void init(SessionFactory factory) {
setSessionFactory(factory);
}
to your GenericDAO.

Related

kafkaendpointlistenerregistry.start() throws null pointer exception

I have a requirement where I want to start Kakfa consumer manually.
Code :
class Dummy implements
ConsumerSeekAware
{
#Autowired
KafkaListenerEndpointRegistry registry;
CountDownLatch latch;
#Autowired
ConcurrentKafkaListenerContainerFactory factory;
onIdleEvent(){
latch.countdown()
}
#KafkaListener(id="myContainer",
topics="mytopic",
autoStartup="false")
public void listen() {}
#Scheduled(cron=" some time ")
void do_some_consumption(){
latch = new CountDownLatch(1);
this.registry.getListenerContainer("myContainer").start();
latch.await();
do processing
this.registry.getListenerContainer("myContainer").stop()
}
}
I have made the bean of
ConcurrentKafkaListenerContainerFactory with all props in my other Config class which I am Autowiring here.
However, I get a null pointer exception when I start my container
using this.registry.getListenerContainer("myContainer").start()
java.lang.NullPointerException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
I just copied your code into a Spring Boot app (which auto configures the factories); and everything works perfectly as expected...
#SpringBootApplication
#EnableScheduling
public class So62412316Application {
public static void main(String[] args) {
SpringApplication.run(So62412316Application.class, args);
}
#Bean
public ApplicationRunner runner(KafkaTemplate<String, String> template) {
return args -> {
template.send("mytopic", "foo");
};
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("mytopic").partitions(1).replicas(1).build();
}
}
#Component
class Dummy implements ConsumerSeekAware {
#Autowired
KafkaListenerEndpointRegistry registry;
CountDownLatch latch;
#Autowired
ConcurrentKafkaListenerContainerFactory factory;
#EventListener
public void onIdleEvent(ListenerContainerIdleEvent event) {
System.out.println(event);
latch.countDown();
}
#KafkaListener(id = "myContainer", topics = "mytopic", autoStartup = "false")
public void listen(String in) {
System.out.println(in);
}
#Scheduled(initialDelay = 5_000, fixedDelay = 60_000)
void do_some_consumption() throws InterruptedException {
latch = new CountDownLatch(1);
this.registry.getListenerContainer("myContainer").start();
latch.await();
this.registry.getListenerContainer("myContainer").stop();
}
}
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.listener.idle-event-interval=5000

Property Source not getting autowired

I am using external property sources for some reason one of the external property source is not getting autowired, receiving null pointer while creating the authenticaion bean
Error Message
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.filechecker.check.Authenticator]: Constructor threw exception; nested exception is java.lang.NullPointerException
Caused by: java.lang.NullPointerException: null
at com.filechecker.check.Authenticator.(Authenticator.java:30) ~[classes!/:0.0.1-SNAPSHOT]
Line no 30:
String username = emailPropertyConfig.getEmailConfig().getUsername();
not working one
#Component
#PropertySource(value="${email.app.properties}",ignoreResourceNotFound = false)
#ConfigurationProperties
public class PropertyEmailConfiguration {
private EmailConfig emailConfig = new EmailConfig();
public EmailConfig getEmailConfig() {
return emailConfig;
}
public void setEmailConfig(EmailConfig emailConfig) {
this.emailConfig = emailConfig;
}
}
#Component
public class Authenticator extends javax.mail.Authenticator {
#Autowired
PropertyEmailConfiguration emailPropertyConfig;
#Autowired
CipherCrypt cipherCrypt;
private PasswordAuthentication authentication;
public Authenticator() throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
String username = emailPropertyConfig.getEmailConfig().getUsername();
String password = cipherCrypt.decrypt(emailPropertyConfig.getEmailConfig().getEncryptPassword());
authentication = new PasswordAuthentication(username, password);
}
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return authentication;
}
}
working one
#Component
#PropertySource(value="${external.app.properties}", ignoreResourceNotFound = true)
#ConfigurationProperties
public class PropertyConfiguration {
private List<FileStructureConfig> fileStructureConfig = new ArrayList();
private List<EmailSendingProperties> emailSendingProperties = new ArrayList();
public List<FileStructureConfig> getFileStructureConfig() {
return fileStructureConfig;
}
public void setFileStructureConfig(List<FileStructureConfig> fileStructureConfig) {
this.fileStructureConfig = fileStructureConfig;
}
public List<EmailSendingProperties> getEmailSendingProperties() {
return emailSendingProperties;
}
public void setEmailSendingProperties(List<EmailSendingProperties> emailSendingProperties) {
this.emailSendingProperties = emailSendingProperties;
}
}
You are trying to access an #Autowired property within the constructor. The property cannot be autowired at this stage.
In order for Spring to "bake the bean", Spring has to create your object (using your constructor), and only afterwards it applys the autowiring mechanism to inject emailPropertyConfig and cipherCrypt. Therefore you cannot access the two #Autowired properties within the constructor.
If you need to extract some values from the emailPropertyConfig or cipherCrypt you can do it in #PostConstruct
#Component
public class Authenticator {
#Autowired
PropertyEmailConfiguration emailPropertyConfig;
#Autowired
CipherCrypt cipherCrypt;
private PasswordAuthentication authentication;
#PostConstruct
void init() throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
String username = emailPropertyConfig.getEmailConfig().getUsername();
String password = cipherCrypt.decrypt(emailPropertyConfig.getEmailConfig().getEncryptPassword());
authentication = new PasswordAuthentication(username, password);
}
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return authentication;
}
}
or use constructor injection:
#Component
public class Authenticator {
PropertyEmailConfiguration emailPropertyConfig;
CipherCrypt cipherCrypt;
private PasswordAuthentication authentication;
public Authenticator(PropertyEmailConfiguration emailPropertyConfig, CipherCrypt cipherCrypt) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
String username = emailPropertyConfig.getEmailConfig().getUsername();
String password = cipherCrypt.decrypt(emailPropertyConfig.getEmailConfig().getEncryptPassword());
authentication = new PasswordAuthentication(username, password);
}
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return authentication;
}
}

Upgraded to Springboot 2 AbstractRoutingDataSource not working

Hi i was using Spring boot 1.4.0 it was working fine.
When i upgraded to Spring boot 2.0.0 its giving below error.
Could not open JDBC Connection for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [null]
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:305)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:378)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:474)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
at com.vega.ui.service.SecurityServiceImpl$$EnhancerBySpringCGLIB$$5fdaff4b.getTenantIdByUserName()
Here is my code to implement this
public class TenantContext {
private static ThreadLocal currentTenant = new ThreadLocal<>();
public static void setCurrentTenant(Object tenant) {
System.out.println("tenant : "+tenant);
currentTenant.set(tenant);
}
public static Object getCurrentTenant() {
return currentTenant.get();
}
public static void removeTenant() {
currentTenant.remove();
}
}
public class TenantRoutingDataSource extends AbstractRoutingDataSource {
String tenantId;
private Map<Object, Object> mRountingTargetDataSources;
public void setRountingTargetDataSources(Map<Object, Object> ptargetDataSources) {
mRountingTargetDataSources = ptargetDataSources;
this.setTargetDataSources(ptargetDataSources);
}
public Map<Object, Object> getRountingTargetDataSources() {
return mRountingTargetDataSources;
}
#Override
protected Object determineCurrentLookupKey() {
if (TenantContext.getCurrentTenant() != null)
return TenantContext.getCurrentTenant();
else {
return tenantId;
}
}
}
#Bean(name = "transactionManager")
#Autowired
public DataSourceTransactionManager transactionManager(
#Qualifier("dbTenantDataSource") TenantRoutingDataSource dataSource) {
logger.info("Inside DataSourceTransactionManager");
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
dataSourceTransactionManager.setRollbackOnCommitFailure(true);
return dataSourceTransactionManager;
}

Not a managed type during app context initialization

I cannot figure out why entity objects cannot be managed by DAO classes.
I have the following PersistenceConfig file:
#Configuration
#EnableTransactionManagement
#PropertySource({"classpath:persistence"})
#EnableJpaRepositories("com.wx.rm")
public class PersistenceConfig {
#Autowired
private Environment env;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(restDataSource());
sessionFactory.setPackagesToScan(new String[]{"com.wx.rm"});
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource restDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
Properties hibernateProperties() {
return new Properties() {
{
setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
setProperty("hibernate.globally_quoted_identifiers",
env.getProperty("hibernate.globally_quoted_identifiers"));
}
};
}
}
Interface IFooAdvertDao:
#Repository("fooAdvertDao")
public interface IFooAdvertDao extends GenericDao<FooAdvert, String> {
}
Interface IBarAdvertDao:
#Repository("barAdvertDao")
public interface IBarAdvertDao extends GenericDao<BarAdvert, Integer> {
}
Base entity BaseAdvert class:
#MappedSuperclass
public abstract class BaseAdvert implements Serializable{
private static final long serialVersionUID = 1L;
#Column(name = "link")
private String link;
#Column(name = "rooms")
private BigDecimal rooms;
public BaseAdvert() {
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public BigDecimal getRooms() {
return rooms;
}
public void setRooms(BigDecimal rooms) {
this.rooms = rooms;
}
}
Entity FooAdvert class:
#Entity
#Table(name = "foo_adverts")
#DynamicUpdate
public class FooAdvert extends BaseAdvert implements Serializable {
#Id
private String id;
#Column(name = "type")
private Integer type;
public FooAdvert() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
}
Entity BarAdvert class:
#Entity
#Table(name = "bar_adverts")
#DynamicUpdate
public class BarAdvert extends BaseAdvert implements Serializable {
#Id
private Integer id;
#Column(name = "status")
private Integer status;
public BarAdvert() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
}
That`s my GenericDao class:
#Repository
public interface GenericDao<T extends Serializable, ID extends Serializable> extends JpaRepository<T, ID> {
}
Generic service class:
public interface GenericService <T extends Serializable, ID extends Serializable> {
T findOne(final ID id);
Page<T> findAll(final Pageable pageable);
long count();
}
Generic service implementation class:
#Service
#Transactional
public class GenericServiceImpl <T extends Serializable, ID extends Serializable> implements GenericService<T, ID> {
#Autowired
private GenericDao<T, ID> genericDao;
public GenericServiceImpl() {
}
public GenericServiceImpl(GenericDao<T, ID> genericDao) {
this.genericDao = genericDao;
}
#Override
#Transactional(readOnly = true)
public T findOne(ID id) {
return genericDao.findOne(id);
}
#Override
#Transactional(readOnly = true)
public Page<T> findAll(Pageable pageable) {
return genericDao.findAll(pageable);
}
#Override
#Transactional(readOnly = true)
public long count() {
return genericDao.count();
}
}
FooAdvertService:
#Service
public class FooAdvertService extends GenericServiceImpl<FooAdvert, String> {
#Autowired
private IFooAdvertDao fooAdvertDao;
}
FooAdvertController class:
#RestController
#RequestMapping("/api")
public class FooAdvertController {
#Autowired
private FooAdvertService fooAdvertService;
#GetMapping("count")
public ResponseEntity getAdvertsCount() {
Long advertsCount = fooAdvertService.count();
return new ResponseEntity(advertsCount, HttpStatus.OK);
}
}
Error message when I try to run my app:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fooAdvertController': Unsatisfied dependency expressed through field 'fooAdvertService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fooAdvertService': Unsatisfied dependency expressed through field 'genericDao'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fooAdvertDao': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class org.wixanz.rm.ads.domain.FooAdvert
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fooAdvertService': Unsatisfied dependency expressed through field 'genericDao'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fooAdvertDao': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class org.wixanz.rm.ads.domain.FooAdvert
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fooAdvertDao': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class org.wixanz.rm.ads.domain.FooAdvert
Caused by: java.lang.IllegalArgumentException: Not a managed type: class org.wixanz.rm.ads.domain.FooAdvert
at org.hibernate.jpa.internal.metamodel.MetamodelImpl.managedType(MetamodelImpl.java:210) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
try to autowire the IFooAdvertDao by name
#Service
public class FooAdvertService extends GenericServiceImpl<FooAdvert, String>
{
#Autowired
#Qualifier("fooAdvertDao")
private IFooAdvertDao fooAdvertDao;
}

Autowired JpaRepository Save Ignored in custom FieldSetMapper

In brief, I am trying to use JpaRepositories in spring batch related classes. But due to some reason, only selects are working but Insert/updates are ignored.
I have a custom FieldSetMapper as below,
#Component
#StepScope // I have not seen step scope is used as this way!
public class ItemFieldSetMapper implements FieldSetMapper<CategoryItem> {
private CSVSchema schema;
private Task task;
Logger logger = Logger.getLogger(LocalPersist.class.getName());
#Autowired
private CSVColumnDao csvColumnDao;
#Autowired
private BasisCategoryDao basisCategoryDao;
#Override
public BasisCategoryItem mapFieldSet(FieldSet fieldSet) throws BindException {
// csvColumnDao.save(someobject) // This is ignored
}
}
Since I have to start the job asynchronously (Job is started form controller method)
I created a custom DefaultBatchConfigurer as below,
#Component
#EnableBatchProcessing
public class ProductImportBatchConfig extends DefaultBatchConfigurer {
#Autowired
private TaskExecutor taskExecutor; // This is coming form another bean defined in another configuration class.
/*#Autowired
private DataSource dataSource;*/
#Autowired
private PlatformTransactionManager transactionManager;
#Override
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(super.getJobRepository());
jobLauncher.setTaskExecutor(taskExecutor);
return jobLauncher;
}
#Override
public PlatformTransactionManager getTransactionManager() {
// TODO Auto-generated method stub
return transactionManager;
}
#Override
#Autowired
public void setDataSource(DataSource dataSource) {
DatabasePopulatorUtils.execute(dropDatabasePopulator(), dataSource);
DatabasePopulatorUtils.execute(createDatabasePopulator(), dataSource);
// TODO Auto-generated method stub
super.setDataSource(dataSource);
}
private DatabasePopulator dropDatabasePopulator() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.setContinueOnError(true);
databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-drop-mysql.sql"));
return databasePopulator;
}
private DatabasePopulator createDatabasePopulator() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.setContinueOnError(true);
databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-mysql.sql"));
return databasePopulator;
}
}
This is my Application class,
#Configuration
#ComponentScan
#EnableAutoConfiguration(exclude = { BatchAutoConfiguration.class })
public class Application extends SpringBootServletInitializer {
private static Class<Application> applicationClass = Application.class;
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected final SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
pool.setCorePoolSize(6);
pool.setMaxPoolSize(30);
pool.setWaitForTasksToCompleteOnShutdown(false);
return pool;
}
}
What I have figured so far:
I went through many documentation to get my scenario working. This is the closest I got.
The issue is to do with #EnableBatchProcessing. It creates its own DataSourceTransactionManager which has no clue about jpa/hibernate and ignore inserts/updates.
As the suggested solution,
#Bean
public BatchConfigurer batchConfigurer(DataSource dataSource, EntityManagerFactory entityManagerFactory) {
return new BasicBatchConfigurer(dataSource, entityManagerFactory);
}
This seems to be not working because the constructor changed in newer versions.
It seems like, I have to get rid of the #EnableBatchProcessing and get the same transaction manager to work both in application and batch contexts.
Here are some trial and errors,
If I keep the #EnableBatchProcessing and use #EnableAutoConfiguration (exclude = { BatchAutoConfiguration.class })
The Batch tables are created and application runs correctly, But during the run time I get the following.
java.lang.StackOverflowError: null
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936) ~[na:1.8.0_72]
at org.springframework.aop.framework.AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport.java:487) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:193) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at com.sun.proxy.$Proxy126.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_72]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_72]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at com.sun.proxy.$Proxy126.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_72]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_72]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at com.sun.proxy.$Proxy126.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_72]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_72]
Here is the properties file I am using,
spring.datasource.url = jdbc:mysql://localhost:3306/db
spring.datasource.username =root
spring.datasource.password =root
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.validationQuery = SELECT 1
spring.datasource.timeBetweenEvictionRunsMillis = 3600
spring.datasource.minEvictableIdleTimeMillis = 3600
spring.datasource.testWhileIdle = true
spring.jpa.database = MYSQL
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto =update
spring.jpa.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming_strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.batch.job.enabled=false
spring.view.prefix=/WEB-INF/jsp/
spring.view.suffix=.jsp
But when I comment #EnableBatchProcessing the application complains for
JobBuilderFactory bean and etc. It seems like use of same transaction manager is not a practice and I have not see auto wiring of Jpa Repositories in batch related classes (Eg, ItemWriters and Readers etc.). But I want this scenario to work since this is a migration from a different implementation to spring batch.
I was confused with the injecting jpa transaction manager into the spring batch. This is how I was able to do it.
#Configuration
#EnableBatchProcessing
public class ProductImportBatchConfig implements BatchConfigurer {
private static final Log logger = LogFactory.getLog(DefaultBatchConfigurer.class);
#Autowired
private TaskExecutor taskExecutor;
#Autowired
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
#Autowired
private LocalContainerEntityManagerFactoryBean entityManagerFactory;
private JobRepository jobRepository;
private JobLauncher jobLauncher;
private JobExplorer jobExplorer;
protected ProductImportBatchConfig() {
}
#Override
public JobRepository getJobRepository() {
return jobRepository;
}
#Override
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
#Override
public JobLauncher getJobLauncher() {
return jobLauncher;
}
#Override
public JobExplorer getJobExplorer() {
return jobExplorer;
}
#Autowired
public void setDataSource(DataSource dataSource) {
if (this.dataSource == null) {
logger.error(null, new Throwable("This is not acceptable"));
}
DatabasePopulatorUtils.execute(dropDatabasePopulator(), this.dataSource);
DatabasePopulatorUtils.execute(createDatabasePopulator(), this.dataSource);
this.dataSource = dataSource;
EntityManagerFactory object = entityManagerFactory.getObject();
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(object);
this.transactionManager = jpaTransactionManager;
}
#PostConstruct
public void initialize() {
try {
if (dataSource == null) {
logger.warn("No datasource was provided...using a Map based JobRepository");
if (this.transactionManager == null) {
this.transactionManager = new ResourcelessTransactionManager();
}
MapJobRepositoryFactoryBean jobRepositoryFactory = new MapJobRepositoryFactoryBean(
this.transactionManager);
jobRepositoryFactory.afterPropertiesSet();
this.jobRepository = jobRepositoryFactory.getObject();
MapJobExplorerFactoryBean jobExplorerFactory = new MapJobExplorerFactoryBean(jobRepositoryFactory);
jobExplorerFactory.afterPropertiesSet();
this.jobExplorer = jobExplorerFactory.getObject();
} else {
this.jobRepository = createJobRepository();
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
jobExplorerFactoryBean.setDataSource(this.dataSource);
jobExplorerFactoryBean.afterPropertiesSet();
this.jobExplorer = jobExplorerFactoryBean.getObject();
}
this.jobLauncher = createJobLauncher();
} catch (Exception e) {
throw new BatchConfigurationException(e);
}
}
public JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(taskExecutor);
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
public JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.afterPropertiesSet();
return factory.getObject();
}
// Batch related scripts
private DatabasePopulator dropDatabasePopulator() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.setContinueOnError(true);
databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-drop-mysql.sql"));
return databasePopulator;
}
private DatabasePopulator createDatabasePopulator() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.setContinueOnError(true);
databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-mysql.sql"));
return databasePopulator;
}
}

Resources