Configuration of chained transaction manager in SDN4 after migration from SDN3 - spring-data-neo4j-4

At the moment I am trying to migrate from SDN3 to SDN4. In my project I use two databases: Neo4j and MySQL, so I end up with chained transaction manager. However, after migration I have problem with its configuration. Before the migration I had this:
#Bean(name = "transactionManager")
#Autowired
public PlatformTransactionManager neo4jTransactionManager(
LocalContainerEntityManagerFactoryBean entityManagerFactory, GraphDatabaseService graphDatabaseService)
throws Exception {
JtaTransactionManager neoTransactionManager = new JtaTransactionManagerFactoryBean(graphDatabaseService)
.getObject();
neoTransactionManager.setRollbackOnCommitFailure(true);
neoTransactionManager.setAllowCustomIsolationLevels(true);
JpaTransactionManager mysqlTransactioNmanager = new JpaTransactionManager(entityManagerFactory.getObject());
return new ChainedTransactionManager(mysqlTransactioNmanager, neoTransactionManager);
}
Now I have something like this:
#Bean(name = "transactionManager")
#Autowired
public PlatformTransactionManager neo4jTransactionManager(
LocalContainerEntityManagerFactoryBean entityManagerFactory, Neo4jTransactionManager neo4jTransactionManager)
throws Exception {
Neo4jTransactionManager neoTransactionManager = neo4jTransactionManager;
JpaTransactionManager mysqlTransactioNmanager = new JpaTransactionManager(entityManagerFactory.getObject());
return new ChainedTransactionManager(mysqlTransactioNmanager, neoTransactionManager);
}
However, project could not be deployed on server, because of this exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public org.springframework.transaction.PlatformTransactionManager com.project.config.ApplicationConfig.neo4jTransactionManager(org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,org.springframework.data.neo4j.transaction.Neo4jTransactionManager) throws java.lang.Exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.data.neo4j.transaction.Neo4jTransactionManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
When mentioned part of configuration is commented project is properly deployed, but obviously there is an exception concerning missing transaction during save to MySQL database.
How should I configure this chained transaction manager in SDN4? It is hard to find any examples now, because SDN4 is quite recent and I really need to have Neo4j in standalone mode, so migration seems to be a good idea.

With this configuration I managed to successfully deploy my application:
#Bean(name = "transactionManager")
#Autowired
public PlatformTransactionManager neo4jTransactionManager(
LocalContainerEntityManagerFactoryBean entityManagerFactory,
Session session) throws Exception {
Neo4jTransactionManager neoTransactionManager = new Neo4jTransactionManager(session);
JpaTransactionManager mysqlTransactioNmanager = new JpaTransactionManager(entityManagerFactory.getObject());
return new ChainedTransactionManager(mysqlTransactioNmanager,neoTransactionManager);
}
I had also to add this element to my config:
#Override
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}

Related

javax.persistence.TransactionRequiredException: Executing an update/delete query : Spring boot Migration from 1.5.4 to 2.7.5

I am migrating the spring boot app from spring boot 1.5.4 to spring boot 2.7.5, In old code I have hibernate at DAO layer. (does the migration caused the issue, I did not find very particular about the issue )
properties file has connection details and used below key
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
Application class have following annotations,
#EnableTransactionManagement #EnableJpaRepositories(basePackages = {"repository package"}) #ComponentScan(basePackages = {"base package"}) #EntityScan(basePackages = {"entity package"})
DAO code :
private EntityManager entityManager;
private Session getSession() {
return entityManager.unwrap(Session.class);
}
// problematic code
public void doSomething(){
String hqlBuilder = "Update Query statement"
Query query = getSession().createQuery(hqlBuilder);
int result = query.executeUpdate();
}```
Service code:
Service class has #Transctional defined at class level however it throws exception on invoking the doSomething() method on DAO instance
`**Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query**
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:445)
at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1692)
at <removed the package path>.dao.impl.LastViewedHistoryElementDaoHibernate.insertOrUpdate(LastViewedHistoryElementDaoHibernate.java:122)
at impl.LastViewedHistoryElementDaoHibernate$$FastClassBySpringCGLIB$$f8fba493.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
... 271 common frames omitted`
**In DAO method returns it throws the TransactionRequiredException : No Transaction in progress**
Thank you, I did applied the #EnableTransactionManagement #Transactional, added spring jpa. I did configure Transaction manager in config also however not luck,
```#Autowired
HikariDataSource dataSource;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setPackagesToScan(
new String[]{"<My packages>"});
//sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public PlatformTransactionManager transactionManager() {
HibernateTransactionManager hibernateTransactionManager
= new HibernateTransactionManager();
hibernateTransactionManager.setSessionFactory(sessionFactory().getObject());
return hibernateTransactionManager;
}```
but still same error as Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:445)
at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:3496)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1399)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1394)
at org.springframework.orm.hibernate5.SessionFactoryUtils.flush(SessionFactoryUtils.java:113)
at org.springframework.orm.hibernate5.SpringSessionSynchronization.beforeCommit(SpringSessionSynchronization.java:95)
at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCommit(TransactionSynchronizationUtils.java:97)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransactionManager.java:916)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:727)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:654)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:407)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)

Error in running spring boot application connected to mongodb

Once I run my code it gives below error. Howenter image description here I can solve it.
Field personRepository in com.example.demo.service.PersonService required a bean named 'mongoTemplate' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean named 'mongoTemplate' in your configuration.
You need to explicitly register bean to spring container.
#Configuration public class AppConfig {
public #Bean MongoClient mongoClient() {
return new MongoClient("localhost"); }
public #Bean MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoClient(), "mydatabase");
} }

I am getting error Table 'test.batch_job_instance' doesn't exist

I am new to Spring Batch. I have configured my job with inmemoryrepository. But still, it seems it is using DB to persist job Metadata.
My spring batch Configuration is :
#Configuration
public class BatchConfiguration {
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
private JobBuilderFactory jobBuilder;
#Bean
public JobLauncher jobLauncher() throws Exception {
SimpleJobLauncher job =new SimpleJobLauncher();
job.setJobRepository(getJobRepo());
job.afterPropertiesSet();
return job;
}
#Bean
public PlatformTransactionManager getTransactionManager() {
return new ResourcelessTransactionManager();
}
#Bean
public JobRepository getJobRepo() throws Exception {
return new MapJobRepositoryFactoryBean(getTransactionManager()).getObject();
}
#Bean
public Step step1(JdbcBatchItemWriter<Person> writer) throws Exception {
return stepBuilderFactory.get("step1")
.<Person, Person> chunk(10)
.reader(reader())
.processor(processor())
.writer(writer).repository(getJobRepo())
.build();
}
#Bean
public Job job( #Qualifier("step1") Step step1) throws Exception {
return jobBuilder.get("myJob").start(step1).repository(getJobRepo()).build();
}
}
How to resolve above issue?
If you are using Sprint boot
a simple property in your application.properties will solve the issue
spring.batch.initialize-schema=ALWAYS
For a non-Spring Boot setup:This error shows up when a datasource bean is declared in the batch configuration. To workaround the problem I added an embedded datasource, since I didn't want to create those tables in the application database:
#Bean
public DataSource mysqlDataSource() {
// create your application datasource here
}
#Bean
#Primary
public DataSource batchEmbeddedDatasource() {
// in memory datasource required by spring batch
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema-drop-h2.sql")
.addScript("classpath:schema-h2.sql")
.build();
}
The initialization scripts can be found inside the spring-batch-core-xxx.jar under org.springframework.batch.core package.Note I used an in-memory database but the solution is valid also for other database systems.
Those who face the same problem with MySql database in CentOS(Most Unix based systems).
Table names are case-sensitive in Linux. Setting lower_case_table_names=1 has solved the problem.
Find official document here
For those using versions greater then spring-boot 2.5 this worked inside of application.properties
spring.batch.jdbc.initialize-schema = ALWAYS
This solved my case:
spring.batch.jdbc.initialize-schema=ALWAYS

UnsatisfiedDependencyException in SpringMVC and MongoDB project

I'm working on a Spring MVC and MongoDB backed application.
My api is working fine,but i would like to create an integration test with Fongo in order to test saving a file and after getting this file by my services.
I have already tried, but when i added #Autowired in my Service class, it returned an UnsatisfiedDependencyException. My test fixture class is the following:
#ActiveProfiles({"test"})
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
locations = {
"classpath*:config/servlet-config.xml",
"classpath*:config/SpringConfig.xml"})
#WebAppConfiguration
public class IntegrationTest {
#Autowired // returns UnsatisfiedDependencyException
private FileService fileService;
#Before
#Test
public void setUp() throws IOException {
Fongo fongo = new Fongo("dbTest");
DB db = fongo.getDB("myDb");
}
#Test
public void testInsertAndGetFile() throws IOException{
//create a file document in order to save
File file = new File();
file.setId("1");
file.setFileName("test1");
file.setVersionId("1");
//save the file
String id = fileService.storeFile(file);
//get the file
File fileDocument= fileService.getFileById("1");
//check if id is the file id which is expected
assertEquals(fileDocument.getId(), "1");
}
}
and i am receiving this log message:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.ots.openonefilesystem.integration.IntegrationTest': Unsatisfied dependency expressed through field 'fileService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean found for dependency [com.myproject.service.FileService]: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean found for dependency [com.myproject.service.FileService]: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I'm using xml configuration.
How can i solve my testing error? In my service class FileService i have put #Service, however it seems that Spring expected at least 1 bean which qualifies as autowire candidate.
Also if i remove the #Autowired, the log returns NullpointerException when saving of getting the file,as it was expected.
PS: I use springFramework 4.3.3.RELEASE, spring-data-mongodb 1.9.5.RELEASE
Thank you in advance!
I had to configure Mock Database (Fongo), in order to #Autowired FileService. This, I made it via #ContextConfiguration(classes = {MockDatasourceConfig.class})
So, the correct test fixture class is the following:
#ActiveProfiles({"test"})
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {MockDatasourceConfig.class})
#WebAppConfiguration
public class IntegrationTest{
#Autowired
private FileService fileService;
#Test
public void testInsertAndGetFile() throws IOException{
//create a file document in order to save
File file = new File();
file.setId("1");
file.setFileName("test1");
file.setVersionId("1");
//save the file and return his id
String id = fileService.storeFileGeneral(file);
//get the file
FileDocument fileDocument= fileService.getFileById(id);
//check that the file isn't null
assertNotNull(fileDocument);
//check if id is the file id which is expected
assertEquals(fileDocument.getId(), id);
assertEquals(fileDocument.getFileName(), "test1");
}
}
and the MockDatasourceConfig class is the following:
#Profile({"test"})
#Configuration
#PropertySource("classpath:mongo.properties")
#ComponentScan(basePackageClasses = {File.class})
#EnableMongoRepositories(basePackageClasses = RepositoryPackageMarker.class)
public class MockDatasourceConfig extends AbstractMongoConfiguration {
#Autowired
Environment env;
#Override
protected String getDatabaseName() {
return env.getProperty("mongo.dbname", "myDb");
}
#Override
public Mongo mongo() throws Exception {
return new Fongo(getDatabaseName()).getMongo();
}
#Bean
public GridFsTemplate gridFsTemplate() throws Exception {
return new GridFsTemplate( mongoDbFactory(),mappingMongoConverter());
}
}
P.S: With helpful advice of #M.Deinum

Morphia, Embed Mongo and Spring. Address already in use

I am trying use MongoDB, Morphia and Spring and test it, so I started use Embedded Mongo.
When I had only one DAO to persist I did not had any problem with my tests, however, in some cases I needed use more than one DAO, and in that cases my injected Datasore give me an problem: addr already in use.
My Spring Test Database Configuration is this:
#Configuration
public class DatabaseMockConfig {
private static final int PORT = 12345;
private MongodConfigBuilder configBuilder;
private MongodExecutable mongodExecutable;
private MongodProcess mongodProcess;
#Bean
#Scope("prototype")
public MongodExecutable getMongodExecutable() {
return this.mongodExecutable;
}
#Bean
#Scope("prototype")
public MongodProcess mongodProcess() {
return this.mongodProcess;
}
#Bean
public IMongodConfig getMongodConfig() throws UnknownHostException, IOException {
if (this.configBuilder == null) {
configBuilder = new MongodConfigBuilder().version(Version.Main.PRODUCTION).net(new Net(PORT, Network.localhostIsIPv6()));
}
return this.configBuilder.build();
}
#Autowired
#Bean
#Scope("prototype")
public Datastore datastore(IMongodConfig mongodConfig) throws IOException {
MongodStarter starter = MongodStarter.getDefaultInstance();
this.mongodExecutable = starter.prepare(mongodConfig);
this.mongodProcess = mongodExecutable.start();
MongoClient mongoClient = new MongoClient("localhost", PORT);
return new Morphia().createDatastore(mongoClient, "morphia");
}
#Autowired
#Bean
#Scope("prototype")
public EventDAO eventDAO(final Datastore datastore) {
return new EventDAO(datastore);
}
#Autowired
#Bean
#Scope("prototype")
public EditionDAO editionDAO(final Datastore datastore) {
return new EditionDAO(datastore);
}
}
And my DAO classes are similar to that
#Repository
public class EventDAO {
private final BasicDAO<Event, ObjectId> basicDAO;
#Autowired
public EventDAO(final Datastore datastore) {
this.basicDAO = new BasicDAO<>(Event.class, datastore);
}
...
}
My test class is similar to that:
#ContextConfiguration(classes = AppMockConfig.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class EventDAOTest {
#Autowired
private EventDAO eventDAO;
#Autowired
private MongodExecutable mongodExecutable;
#Autowired
private MongodProcess mongodProcess;
#Rule
public ExpectedException expectedEx = ExpectedException.none();
#After
public void tearDown() {
this.mongodProcess.stop();
this.mongodExecutable.stop();
}
...
}
I use prototype scope to solve problem with singleton and make sure that my mock database is clean when I start my test, after that I stop mongod process and mongod executable.
However since I need use more than one DAO I receive that error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'editionDAO' defined in class br.com.mymusicapp.spring.DatabaseMockConfig: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.mongodb.morphia.Datastore]: :
Error creating bean with name 'datastore' defined in class br.com.mymusicapp.spring.DatabaseMockConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.mongodb.morphia.Datastore]:
Factory method 'datastore' threw exception; nested exception is java.io.IOException: Could not start process: ERROR: listen(): bind() failed errno:98 Address already in use for socket: 0.0.0.0:12345
2015-01-04T01:05:04.128-0200 [initandlisten] ERROR: addr already in use
I know what the error means, I just do not know how can I design my Configuration to solve that. As last option I am considering install a localhost MongoDB just for tests, however I think could be a better solution
That is based on the embedded mongod by flapdoodle, right?
If you want to run multiple tests in parallel (could be changed via JUnit annotations, but it's probably faster in parallel), you cannot use a single, hardcoded port. Instead, let the embedded process select an available port automatically.