I am using scheduled task to update my database like this:
public interface UserRatingManager {
public void updateAllUsers();
}
@Service
public class DefaultUserRatingManager implements UserRatingManager {
@Autowired
UserRatingDAO userRatingDAO;
@Override
@Transactional("txName")
public void updateAllUsers() {
List<String> userIds = userRatingDAO.getAllUserIds();
for (String userId : userIds) {
updateUserRating(userId);
}
}
}
public interface UserRatingDAO extends GenericDAO<UserRating, String> {
public void deleteAll();
public List<String> getAllUserIds();
}
@Repository
public class HibernateUserRatingDAO extends BaseDAO<UserRating, String> implements UserRatingDAO {
@Override
public List<String> getAllUserIds() {
List<String> result = new ArrayList<String>();
Query q1 = getSession().createQuery("Select userId from UserRating");
}
}
I configured the persistence like this:
@Configuration
@ComponentScan({ "com.estartup" })
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement
@EnableScheduling
public class PersistenceConfig {
@Autowired
Environment env;
@Scheduled(fixedRate = 5000)
public void run() {
userRatingManager().updateAllUsers();
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(env.getProperty("connection.url"), env.getProperty("connection.username"), env.getProperty("connection.password"));
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
return driverManagerDataSource;
}
public PersistenceConfig() {
super();
}
@Bean
public UserRatingUpdate userRatingUpdate() {
return new UserRatingUpdate();
}
@Bean
public UserRatingManager userRatingManager() {
return new DefaultUserRatingManager();
}
@Bean
public LocalSessionFactoryBean runnableSessionFactory() {
LocalSessionFactoryBean factoryBean = null;
try {
factoryBean = createBaseSessionFactory();
} catch (Exception e) {
e.printStackTrace();
}
return factoryBean;
}
private LocalSessionFactoryBean createBaseSessionFactory() throws IOException {
LocalSessionFactoryBean factoryBean;
factoryBean = new LocalSessionFactoryBean();
Properties pp = new Properties();
pp.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
pp.setProperty("hibernate.max_fetch_depth", "3");
pp.setProperty("hibernate.show_sql", "false");
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[] { "com.estartup.*" });
factoryBean.setHibernateProperties(pp);
factoryBean.afterPropertiesSet();
return factoryBean;
}
@Bean(name = "txName")
public HibernateTransactionManager runnableTransactionManager() {
HibernateTransactionManager htm = new HibernateTransactionManager(runnableSessionFactory().getObject());
return htm;
}
}
However, when I get to:
Query q1 = getSession().createQuery("Select userId from UserRating");
in the above HibernateUserRatingDAO
I get an exception:
org.hibernate.HibernateException: createQuery is not valid without active transaction
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:352)
at com.sun.proxy.$Proxy63.createQuery(Unknown Source)
at com.estartup.dao.impl.HibernateUserRatingDAO.getAllUserIds(HibernateUserRatingDAO.java:36)
How can I configure to include my scheduled tasks in transactions ?
EDITED:
Here is the code for BaseDAO
@Repository
public class BaseDAO<T, ID extends Serializable> extends GenericDAOImpl<T, ID> {
private static final Logger logger = LoggerFactory.getLogger(BaseDAO.class);
@Autowired
@Override
public void setSessionFactory(SessionFactory sessionFactory) {
super.setSessionFactory(sessionFactory);
}
public void setTopAndForUpdate(int top, Query query){
query.setLockOptions(LockOptions.UPGRADE);
query.setFirstResult(0);
query.setMaxResults(top);
}
EDITED
Enabling Spring transaction prints the following log:
DEBUG [pool-1-thread-1] org.springframework.transaction.annotation.AnnotationTransactionAttributeSource - Adding transactional method 'updateAllUsers' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; 'txName'
What is happening in this case is that since you are using userRatingManager()
inside the configuration (where the actual scheduled method exists), the proxy that Spring creates to handle the transaction management for UserRatingUpdate
is not being used.
I propose you do the following:
public interface WhateverService {
void executeScheduled();
}
@Service
public class WhateverServiceImpl {
private final UserRatingManager userRatingManager;
@Autowired
public WhateverServiceImpl(UserRatingManager userRatingManager) {
this.userRatingManager = userRatingManager;
}
@Scheduled(fixedRate = 5000)
public void executeScheduled() {
userRatingManager.updateAllUsers()
}
}
Also change your transaction manager configuration code to:
@Bean(name = "txName")
@Autowired
public HibernateTransactionManager runnableTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory);
return htm;
}
and remove factoryBean.afterPropertiesSet();
from createBaseSessionFactory
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With