I'm using Spring + Jpa and I'd like to have EntityManager into my #Configuration class.
Now my class is something like this:
#Configuration
#PropertySource("classpath:base.properties")
public class Config {
private static final Logger log = Logger.getLogger(Config.class);
#Bean
public SpringContextManager contextManager() {
return new SpringContextManager(new DefaultApplication());
}
#Bean(initMethod = "start", destroyMethod = "stop")
public ServerSession serverSession() throws Exception {
try {
ServerSession serverSession = new ServerSession(urlGateway, useSsl, hostGateway, portGateway);
serverSession.setDefaultTimeToLive(5000);
return serverSession;
} catch (Throwable e) {
log.error("", e);
return null;
}
}
#Bean
public PluginManager pluginManager() {
PluginManager pluginManager = new PluginManager();
ThreadLocalManager.set(pluginManager);
return pluginManager;
}
I know that I can't add #PersistenceContext to #Configuration class, so I don't know how to get entityManager at this point.
The goal of this is have entityManager asap the app start because I need to set it into a ThreadLocal class ( i need this class to use entityManager inside a JPA entitylistener where inject of persistenceContext don't work).
Now I'm getting the entityManager from a service annotated with #Service but it would be cleaner to made this settings into #Configuration class. Seems more clean.
Thanks for your help.
I found a nice example to solve my problem. This is the link of the tutorial: link
Related
I have a code base which is using for two different applications. some of my spring service classes has annotation #Transactional. On server start I would like to disable #Transactional based on some configuration.
The below is my configuration Class.
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:application.properties")
public class WebAppConfig {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
#Resource
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(url);
dataSource.setUsername(userId);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public PlatformTransactionManager txManager() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
if(appName.equqls("ABC")) {
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER);
}else {
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
}
CustomDataSourceTransactionManager txM=new CustomDataSourceTransactionManager(def);
txM.setDataSource(dataSource());
return txM;
}
#Bean
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
}
I am trying to ovveried methods in DataSourceTransactionManager to make the functionality. But still it is trying to commit/rollback the transaction at end of transaction. Since there is no database connection available it is throwing exception.
If I keep #Transactional(propagation=Propagation.NEVER), everything works perfectly, but I cannot modify it as another app is using the same code base and it is necessary in that case.
I would like to know if there is a to make transaction fully disable from configuration without modifying #Transactional annotation.
I'm not sure if it would work but you can try to implement custom TransactionInterceptor and override its method that wraps invocation into a transaction, by removing that transactional stuff. Something like this:
public class NoOpTransactionInterceptor extends TransactionInterceptor {
#Override
protected Object invokeWithinTransaction(
Method method,
Class<?> targetClass,
InvocationCallback invocation
) throws Throwable {
// Simply invoke the original unwrapped code
return invocation.proceedWithInvocation();
}
}
Then you declare a conditional bean in one of #Configuration classes
// assuming this property is stored in Spring application properties file
#ConditionalOnProperty(name = "turnOffTransactions", havingValue = "true"))
#Bean
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(
/* default bean would be injected here */
TransactionAttributeSource transactionAttributeSource
) {
TransactionInterceptor interceptor = new NoOpTransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
return interceptor;
}
Probably you gonna need additional configurations, I can't verify that right now
I have built an entity listener but have not figured out how to register it so that it will get called. This all runs, and I verified in the debugger that the
registration code executes (apparently successfully) at startup. But the debugger never stops in the listener code.
This is my listener:
public class DirtyAwareListener implements PostLoadEventListener
{
#Override
public void onPostLoad(PostLoadEvent postLoadEvent)
{
if (postLoadEvent.getEntity() instanceof DirtyAware)
{
((DirtyAware)postLoadEvent.getEntity()).commitFields();
}
}
}
and this is the registration component:
#Component
public class HibernateListenerConfigurer
{
#PersistenceUnit
private EntityManagerFactory emf;
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
protected void init()
{
DirtyAwareListener listener = new DirtyAwareListener();
// SessionFactoryImpl sessionFactory = emf.unwrap(SessionFactoryImpl.class);
EventListenerRegistry registry = ((SessionFactoryImpl)sessionFactory).getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.POST_LOAD).appendListener(listener);
}
}
Here is how my general Hibernate configuration code generates a session factory:
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSource());
sessionFactory.setPackagesToScan("com.my.entities");
sessionFactory.setHibernateProperties(getHibernateProperties());
sessionFactory.setEntityInterceptor(new DirtyAwareInterceptor());
return sessionFactory;
Note that the interceptor does work as expected (but unfortunately does not have hooks where I need them.)
To add entity listeners implement org.hibernate.integrator.spi.Integrator. See example https://www.boraji.com/hibernate-5-event-listener-example
I got this working as desired using the Integrator approach as Anton suggested. The link provided in his answer did not provide sufficient information for me to get this to work - I had to reference multiple posts and also do a bit of trial and error. Since I could not find a single post which provided the info, here is how I did it:
The listener code is the same as the above. The Configurer code is not needed - I deleted it. Here is the new Integrator code:
#Component
public class EventListenerIntegrator implements Integrator
{
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry)
{
EventListenerRegistry eventListenerRegistry =
sessionFactoryServiceRegistry.getService(EventListenerRegistry.class);
DirtyAwareListener t = new DirtyAwareListener();
eventListenerRegistry.getEventListenerGroup(EventType.POST_LOAD).appendListener(t);
}
#Override
public void disintegrate(SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {}
}
And here is the revised getSessionFactory method on my #Configuration class:
private static SessionFactory sessionFactory = null;
#Bean
public SessionFactory getSessionFactory()
{
if (sessionFactory == null)
{
BootstrapServiceRegistry bootstrapRegistry =
new BootstrapServiceRegistryBuilder()
.applyIntegrator(new EventListenerIntegrator())
.build();
StandardServiceRegistryBuilder registryBuilder =
new StandardServiceRegistryBuilder(bootstrapRegistry);
registryBuilder.applySetting(org.hibernate.cfg.Environment.DATASOURCE, getDataSource());
registryBuilder.applySettings(getHibernateProperties());
StandardServiceRegistry registry = registryBuilder.build();
MetadataSources sources = new MetadataSources(registry).addPackage("com.my.entities");
sources.addAnnotatedClass(User.class);
Metadata metadata = sources.getMetadataBuilder().build();
sessionFactory = metadata.getSessionFactoryBuilder().build();
}
return sessionFactory;
}
Note: I think the addPackage call is not needed and does not do anything. I had hoped it would do the package scan the old code was doing, but it does not do that. I simply changed that to explicity add each annotated class.
I'm trying to move away from manually-managed transactions to annotation based transactions in my Neo4j application.
I've prepared annotation-based Spring configuration file:
#Configuration
#EnableNeo4jRepositories("xxx.yyy.neo4jplanetspersistence.repositories")
#ComponentScan(basePackages = "xxx.yyy")
#EnableTransactionManagement
public class SpringDataConfiguration extends Neo4jConfiguration
implements TransactionManagementConfigurer{
public SpringDataConfiguration() {
super();
setBasePackage(new String[] {"xxx.yyy.neo4jplanetspojos"});
}
#Bean
public GraphDBFactory graphDBFactory(){
GraphDBFactory graphDBFactory = new GraphDBFactory();
return graphDBFactory;
}
#Bean
public GraphDatabaseService graphDatabaseService() {
return graphDBFactory().getTestGraphDB(); //new GraphDatabaseFactory().newEmbeddedDatabase inside
}
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return neo4jTransactionManager(graphDatabaseService());
}
}
I've marked my repositories with #Transactional:
#Transactional
public interface AstronomicalObjectRepo extends
GraphRepository<AstronomicalObject>{
}
I've marked my unit test classes and test methods with #Transactional and commented old code that used to manually manage transactions:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringDataConfiguration.class},
loader = AnnotationConfigContextLoader.class)
#Transactional
public class AstronomicalObjectRepoTest {
#Autowired
private AstronomicalObjectRepo repo;
#Autowired
private Neo4jTemplate neo4jTemplate;
(...)
#Test #Transactional
public void testSaveAndGet() {
//try (Transaction tx =
//neo4jTemplate.getGraphDatabaseService().beginTx()) {
AstronomicalObject ceres = new AstronomicalObject("Ceres",
1.8986e27, 142984000, 9.925);
repo.save(ceres); //<- BANG! Exception here
(...)
//tx.success();
//}
}
After that change the tests do not pass.
I receive:
org.springframework.dao.InvalidDataAccessApiUsageException: nested exception is org.neo4j.graphdb.NotInTransactionException
I have tried many different things (explicitly naming transaction manager in #Transactional annotation, changing mode in #EnableTransactionManagment...), nothing helped.
Will be very grateful for a clue about what I'm doing wrong.
Thanks in advance!
I found the reason...
SDN does not support newest Neo4j in the terms of transaction.
I believe it is because SpringTransactionManager in neo4j-kernel has gone in 2.2+ releases, but not 100% sure.
On github we can see that 7 hours ago the change was made to fix it:
https://github.com/spring-projects/spring-data-neo4j/blob/master/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/JtaTransactionManagerFactoryBean.java
A quick fix that worked for me was to override neo4jTransactionManager method from Neo4jConfiguration in my configuration, using Neo4jEmbeddedTransactionManager class:
#Override
public PlatformTransactionManager neo4jTransactionManager(GraphDatabaseService graphDatabaseService) {
Neo4jEmbeddedTransactionManager newTxMgr = new Neo4jEmbeddedTransactionManager(graphDatabaseService());
UserTransaction userTransaction = new UserTransactionAdapter( newTxMgr );
return new JtaTransactionManager( userTransaction, newTxMgr );
}
Consider the typical DBUnit Spring Test (see https://github.com/springtestdbunit/spring-test-dbunit) :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:/META-INF/spring/applicationContext-database.xml",
"classpath:spring-*.xml"
})
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
#DatabaseSetup("/dbunit/data.xml")
public class UnitTest {
#Autowired
private UnitUnderTest uut;
#Test
public void shouldInitDB() {
...
}
}
What I have verified is that, and has expected, Autowiring will happen before DatabaseSetup.
This must happen because DBUnit depends on the application context to provide the configured data source.
The problem is that the UnitUnderTest bean has a #PostConstruct where it loads some data from the DB but, since the Autowiring happens before the DBunit setup, the data will not be available at this stage.
Any ideas on how to solve this issue in a clean way?
You can you Spring's ResourceDatabasePopulator.
I think you can use something like this
#PostConstruct
public void myInMemryPopulator() {
final ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
try {
Resource[] array = resourceResolver.getResources("classpath:/*.sql");
for (Resource resource : array) {
databasePopulator.addScript(resource);
}
databasePopulator.populate(dataSource.getConnection());
} catch (IOException | SQLException e) {
LOGGER.error("Error in databasePopulator {} ", e);
}
}
You can have a setup method in your test class and call the post construct method manually. That will work.
Lazy initialization helped me, I added #Lazy over the #Component* and over the #Autowired in injection points
#Lazy
#Component
public class UnitUnderTestImpl {
...
}
#Lazy
#Autowired
private UnitUnderTest uut;
I wonder if there is a way to get #Required working when doing the configuration by annotations. I turned my configuration up-and-down and back again but nothing seems to work for me. I'm using Spring 3.1
My basic configuration looks like this:
#Configuration
public class SpringConfig {
#Bean
public MailSender mailSender() {
MailSender MailSender = new MailSender();
// mailSender.setBean(dlMailSender);
return mailSender;
}
#Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
// setting som props
return myBean;
}
}
MailSender is here:
#Configurable
public class MailSender {
private MyBean myBean;
#Required
public void setMyBean(MyBean myBean) {
this.myBean = myBean;
}
}
I'm testing it with this junit:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SpringConfig.class }, loader = AnnotationConfigContextLoader.class)
public class MailSenderTest {
#Test
public void test_main_beans_exists() {
// when then given
}
}
Thanks for any help
Short answer - this is not even theoretically possible.
When using XML-based, bean definitions with their dependencies are completely managed by application context. Spring is able to check, what is being set and what is not being set.
When using annotation-based configuration, you are setting the dependencies yourself. There is no way how Spring can even know what you are doing with the bean before returning it from the factory method.
If you want to check whether the bean is correctly initialized, use InitializingBean or #PostConstruct and implement self-checking method. Spring is doing this regularly in its own beans.