I want to read text data fixtures (CSV files) at the start on my application and put it in my database.
For that, I have created a PopulationService with an initialization method (#PostConstruct annotation).
I also want them to be executed in a single transaction, and hence I added #Transactional on the same method.
However, the #Transactional seems to be ignored :
The transaction is started / stopped at my low level DAO methods.
Do I need to manage the transaction manually then ?
Quote from legacy (closed) Spring forum:
In the #PostConstruct (as with the afterPropertiesSet from the InitializingBean interface) there is no way to ensure that all the post processing is already done, so (indeed) there can be no Transactions. The only way to ensure that that is working is by using a TransactionTemplate.
So if you would like something in your #PostConstruct to be executed within transaction you have to do something like this:
#Service("something")
public class Something {
#Autowired
#Qualifier("transactionManager")
protected PlatformTransactionManager txManager;
#PostConstruct
private void init(){
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//PUT YOUR CALL TO SERVICE HERE
}
});
}
}
I think #PostConstruct only ensures the preprocessing/injection of your current class is finished. It does not mean that the initialization of the whole application context is finished.
However you can use the spring event system to receive an event when the initialization of the application context is finished:
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
// do startup code ..
}
}
See the documentation section Standard and Custom Events for more details.
As an update, from Spring 4.2 the #EventListener annotation allows a cleaner implementation:
#Service
public class InitService {
#Autowired
MyDAO myDAO;
#EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event) {
event.getApplicationContext().getBean(InitService.class).initialize();
}
#Transactional
public void initialize() {
// use the DAO
}
}
Inject self and call through it the #Transactional method
public class AccountService {
#Autowired
private AccountService self;
#Transactional
public void resetAllAccounts(){
//...
}
#PostConstruct
private void init(){
self.resetAllAccounts();
}
}
For older Spring versions which do not support self-injection, inject BeanFactory and get self as beanFactory.getBean(AccountService.class)
EDIT
It looks like that since this solution has been posted 1.5 years ago developers are still under impression that if a method,
annotated with #Transactional, is called from a #PostContruct-annotated method invoked upon the Bean initialization, it won't be actually executed inside of Spring Transaction, and awkward (obsolete?) solutions get discussed and accepted instead of this very simple and straightforward one and the latter even gets downvoted.
The Doubting Thomases :) are welcome to check out an example Spring Boot application at GitHub which implements the described above solution.
What actually causes, IMHO, the confusion: the call to #Transactional method should be done through a proxied version of a Bean where such method is defined.
When a #Transactional method is called from another Bean, that another Bean usually injects this one and invokes its proxied (e.g. through #Autowired) version of it, and everything is fine.
When a #Transactional method is called from the same Bean directly, through usual Java call, the Spring AOP/Proxy machinery is not involved and the method is not executed inside of Transaction.
When, as in the suggested solution, a #Transactional method is called from the same Bean through self-injected proxy (self field), the situation is basically equivalent to a case 1.
#Platon Serbin's answer didn't work for me. So I kept searching and found the following answer that saved my life. :D
The answer is here No Session Hibernate in #PostConstruct, which I took the liberty to transcribe:
#Service("myService")
#Transactional(readOnly = true)
public class MyServiceImpl implements MyService {
#Autowired
private MyDao myDao;
private CacheList cacheList;
#Autowired
public void MyServiceImpl(PlatformTransactionManager transactionManager) {
this.cacheList = (CacheList) new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
#Override
public Object doInTransaction(TransactionStatus transactionStatus) {
CacheList cacheList = new CacheList();
cacheList.reloadCache(MyServiceImpl.this.myDao.getAllFromServer());
return cacheList;
}
});
}
The transaction part of spring might not be initialized completely at #PostConstruct.
Use a listener to the ContextRefreshedEvent event to ensure, that transactions are available:
#Component
public class YourService
implements ApplicationListener<ContextRefreshedEvent> // <= ensure correct timing!
{
private final YourRepo repo;
public YourService (YourRepo repo) {this.repo = repo;}
#Transactional // <= ensure transaction!
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repo.doSomethingWithinTransaction();
}
}
Using transactionOperations.execute() in #PostConstruct or in #NoTransaction method both works
#Service
public class ConfigurationService implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationService.class);
private ConfigDAO dao;
private TransactionOperations transactionOperations;
#Autowired
public void setTransactionOperations(TransactionOperations transactionOperations) {
this.transactionOperations = transactionOperations;
}
#Autowired
public void setConfigurationDAO(ConfigDAO dao) {
this.dao = dao;
}
#PostConstruct
public void postConstruct() {
try { transactionOperations.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(final TransactionStatus status) {
ResultSet<Config> configs = dao.queryAll();
}
});
}
catch (Exception ex)
{
LOG.trace(ex.getMessage(), ex);
}
}
#NoTransaction
public void saveConfiguration(final Configuration configuration, final boolean applicationSpecific) {
String name = configuration.getName();
Configuration original = transactionOperations.execute((TransactionCallback<Configuration>) status ->
getConfiguration(configuration.getName(), applicationSpecific, null));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
}
}
I wrote an EJB scheduler that worked a few days ago, now it doesn't work.
I tried to delete the contents of the / wildfly / standalone / data / timer-service-data directory but the scheduler does not work.
This is the code of my EJB:
#Singleton
public class MyTimerService {
#Resource
private TimerService timerService;
/** The Constant logger. */
private static final Logger logger = Logger.getLogger(MyTimerService.class);
#PostConstruct
public void initialize() {
logger.info("MyTimerService initialization");
ScheduleExpression scheduleExpression = new ScheduleExpression().hour("*").minute( "*/5");
// Persistent must be set to false (since it defaults to true) because the timer is not specific to this JVM.
TimerConfig test = new TimerConfig("START TIMER", false);
timerService.createCalendarTimer(scheduleExpression, test);
}
#Timeout
public void scheduler(Timer timer) {
//my logic here ...
}
#PreDestroy
public void stop() {
logger.info("SingletonTimer is stopping: the server is either being shutdown or another node has become elected to be the singleton master.");
}
My code looks correct, maybe it's a server problem?
EDIT:
I added #Startup and now it works :)
The specific reason I know is because the QuartzJob class is instantiated, but I know that the constructor with no arguments is used, and then the execute method is called to report the exception. #Inject does not work.
Printed error message:
org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: java.lang.NullPointerException
at org.openscoring.service.quartz.QuartzJob.execute(QuartzJob.java:36)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
... 1 more
QuartzJob implements the Job interface
#Service
#Singleton
public class QuartzJob implements Job {
#Inject
private SynchronousCache synchronousCache;
#Override
public void execute(JobExecutionContext content) {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+ "★★★★★★★★★★★");
synchronousCache.synchronizationModel();
}
}
QuartzManager.addJob("test", QuartzJob.class, "10/59 * * * * ? *");
Add a timed task
I am having a strange problem with a custom jpa-entity listener I've created in a spring boot application. I'm trying to use Springs #Configurable mechanism to configure the EntityListener (as seen in Springs AuditingEntityListener) but Spring refuses to recognize my Listener as soon as it is used in the #EntityListeners-Annotation on a jpa entity. if it is not configured on a jpa entity, the Listener gets wired/configured by Spring as it should.
I've created an example project with a junit-test to demonstrate the problem:
https://github.com/chrisi/aopconfig/find/master
#SpringBootApplication
#EnableSpringConfigured
#EnableLoadTimeWeaving
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
The EntityListener:
/**
* This bean will NOT be instanciated by Spring but it should be configured by Spring
* because of the {#link Configurable}-Annotation.
* <p>
* The configuration only works if the <code>UnmanagedBean</code> is not used as an <code>EntityListener</code>
* via the {#link javax.persistence.EntityListeners}-Annotation.
*
* #see FooEntity
*/
#Configurable
public class UnmanagedBean {
#Autowired
private ManagedBean bean;
public int getValue() {
return bean.getValue();
}
}
The Bean I want to be injected in the EntityListener/UnmanagedBean:
/**
* This bean will be instanciated/managed by Spring and will be injected into the
* {#link UnmanagedBean} in the case the <code>UnmanagedBean</code> is not used as an JPA-EntityListener.
*/
#Component
#Data
public class ManagedBean {
private int value = 42;
}
The Entity where the Listener should be used:
/**
* This simple entity's only purpose is to demonstrate that as soon as
* it is annotated with <code>#EntityListeners({UnmanagedBean.class})</code>
* springs configurable mechanism will not longer work on the {#link UnmanagedBean}
* and therefore the <code>ConfigurableTest.testConfigureUnmanagedBean()</code> fails.
*/
#Entity
#EntityListeners({UnmanagedBean.class}) // uncomment to make the test fail
public class FooEntity {
#Id
private Long id;
private String bar;
}
And finally the test that shows that the wiring is not working as soon as the Listener is used:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
public class ConfigurableTest {
/**
* This test checks if the ManagedBean was injected into the UnmanagedBean
* by Spring after it was created with <code>new</code>
*/
#Test
public void testConfigureUnmanagedBean() {
UnmanagedBean edo = new UnmanagedBean();
int val = edo.getValue();
Assert.assertEquals(42, val);
}
}
The junit-test (the wiring of the EntityListener/ManagedBean) fails as soon as
the annotation #EntityListeners({UnmanagedBean.class}) in FooEntity is activated.
Is this a bug or did I miss something else?
In order to run the test you have to use -javaagent:spring-instrument-4.1.6.RELEASE.jar on the commandline an provide the jar file in the working directory.
This is the "condensed" version of a question I asked earlier:
#Configurable not recognized in SpringBoot Application
What is the Jersey 2 equivalent of FEATURE_NORMALIZE_URI?
Answering my own question: It doesn't look like Jersey 2 has an equivalent feature, but it's quite simple to implement yourself:
/**
* Normalizes incoming URIs.
* <p>
* #author Gili Tzabari
*/
#PreMatching
public class NormalizeUriFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
URI requestUri = requestContext.getUriInfo().getRequestUri();
URI normalized = requestUri.normalize();
if (!requestUri.equals(normalized))
requestContext.setRequestUri(normalized);
}
}
Don't forget to register() the filter in your Application.