Spring Data Neo4j InvalidDataAccessApiUsageException: Transaction is not current for this thread - spring

I have a Spring boot neo4j application and want to store users in the Neo4j database. I followed the instructions found here.
The neo4j configuration is this:
#Configuration
#EnableWebMvc
#ComponentScan({"eu.bm"})
#EnableNeo4jRepositories("eu.bm.repository")
#EnableTransactionManagement
public class bmConfiguration extends WebMvcConfigurerAdapter {
#Bean
public OpenSessionInViewInterceptor openSessionInViewInterceptor() {
OpenSessionInViewInterceptor openSessionInViewInterceptor =
new OpenSessionInViewInterceptor();
openSessionInViewInterceptor.setSessionFactory(sessionFactory());
return openSessionInViewInterceptor;
}
public void addInterceptors(InterceptorRegistry registry) {
registry.addWebRequestInterceptor(openSessionInViewInterceptor());
}
#Bean
public static SessionFactory sessionFactory() {
return new SessionFactory("eu.bm.domain");
}
#Bean
public Neo4jTransactionManager transactionManager() throws Exception {
return new Neo4jTransactionManager(sessionFactory());
}
The user repository is this:
#Repository
public interface UserRepository extends GraphRepository<User>{
#Query("MATCH (user:User) where user.name={0} return user ")
User findUser(String username);
#Query("MATCH (user:User) where user.name={0} delete user ")
User deleteUser(String username);
#Query("match (user:User) delete user")
User deleteAllUsers();
}
I also have this User management service setup:
#Component
public interface UserManagementService {
List<User> listAll();
User save(User user);
User findUser(String username);
}
which is implemented here:
#Service
#Transactional
public class UserManagementServiceImpl implements UserManagementService {
private UserRepository userRepository;
#Autowired
public UserManagementServiceImpl(UserRepository userRepository) {this.userRepository = userRepository;}
#Override
public List<User> listAll() {
List<User> users = new ArrayList<>();
userRepository.findAll().forEach(users::add);
return users;
}
#Override
public User save(User user) {
userRepository.save(user);
return user;
}
#Override
public User findUser(String username) {
return userRepository.findUser(username);
}
I then perform a simple write-read test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class DatabaseConnectionTest {
// User 1
private static final String UNAME1 = "Paul";
private static final String EMAIL1 = "paul#user.com";
private static final String PASSWORD1 = "p#ss";
private static final String USERNAME1 = "paul";
#Autowired
private UserRepository userRepository;
#Before
public void setUp() throws Exception {
// Clear database before adding new user
userRepository.deleteAllUsers();
// Creating user
User user = new User(UNAME1, USERNAME1, PASSWORD1, EMAIL1);
userRepository.save(user);
}
#Test
public void testPersistence() {
Assert.assertEquals(UNAME1, userRepository.findUser(UNAME1).getName());
Assert.assertEquals(USERNAME1, userRepository.findUser(UNAME1).getUsername());
}
The result of the above is this error:
2017-08-16 14:59:38.990 INFO 6496 --- [ main] e.b.repository.DatabaseConnectionTest : Started DatabaseConnectionTest in 7.131 seconds (JVM running for 8.331)
2017-08-16 14:59:39.872 INFO 6496 --- [ main] o.n.o.drivers.http.request.HttpRequest : Thread: 1, url: http://localhost:7474/db/data/transaction/39, request: {"statements":[{"statement":"UNWIND {rows} as row CREATE (n:User) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, row.type as type","parameters":{"rows":[{"nodeRef":-1821370276,"type":"node","props":{"password":"p#ss","name":"Paul","email":"paul#user.com","username":"paul"}}]},"resultDataContents":["row"],"includeStats":false}]}
2017-08-16 14:59:40.358 ERROR 6496 --- [ main] o.s.d.n.t.Neo4jTransactionManager : Commit exception overridden by rollback exception
org.springframework.dao.InvalidDataAccessApiUsageException: Transaction is not current for this thread; nested exception is org.neo4j.ogm.exception.TransactionManagerException: Transaction is not current for this thread
Why does userRepository.save(user) creates the record in the database but throws the exception?
What does "Transaction is not current for this thread" excactly mean?
Why does userRepository.save(user) ultimately failes but userRepository.deleteAllUsers() works?
edit: My dependencies.gradle includes the following:
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-neo4j', version: '1.5.6.RELEASE'
//compile group: 'org.springframework.data', name: 'spring-data-neo4j', version: '4.2.6.RELEASE'
compile group: 'org.neo4j', name: 'neo4j-ogm-core', version: '2.1.3'
//compile group: 'org.neo4j', name: 'neo4j-ogm-bolt-driver', version: '2.1.3'
runtime group: 'org.neo4j', name: 'neo4j-ogm-http-driver', version: '2.1.3'
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: '1.5.4.RELEASE'
testCompile('org.springframework.boot:spring-boot-starter-test')

Your SessionFactory bean should not be declared as static:
#Bean
public static SessionFactory sessionFactory() {
return new SessionFactory("eu.bm.domain");
}
should be
#Bean
public SessionFactory sessionFactory() {
return new SessionFactory("eu.bm.domain");
}
Background
"Transaction is not current for this thread" means that something is trying to commit a transaction which is different than current tx in a thread local context.
You are using default transaction management - it creates a transaction around repository calls.
When the SessionFactory bean is defined as static the Session somehow doesn't see the transaction in thread local (the one started by the default tx management) and creates new one, tries to commit it and the exception appears.

Related

Using multiple keyspaces working with Spring Boot 2.4.0/Spring Data Cassandra 3.1.1

I am working on a spring boot application that uses two different keyspaces in a cassandra database. In the process of trying to upgrade from spring boot 2.1.7-RELEASE to version 2.4.0, I have found that my java-based configuration no longer works. It seems to be much more difficult than before to extend AbstractCassandraConfiguration due to creation of multiple beans of the same type and ambiguity caused by having multiple cassandra/cql session factories.
All of the examples of a multi-keyspace configuration I have found online are for older versions of Spring Boot, so I am wondering if anyone has figured out a clean way to get this working in the more recent versions?
I migrated my project to using spring-data-cassandra 3.x. I resolved issues with conflicting beans by overriding CqlSessionFactoryBean bean in configurations and adding #Primary annotation for primary keyspace cassandra session bean. My project is using following gradle dependencies:
compile group: 'org.springframework.data', name: 'spring-data-cassandra', version: '3.0.5.RELEASE'
compile group: 'com.datastax.oss', name: 'java-driver-core', version: '4.6.1'
Cassandra configurations classes:
public abstract class AbstractCassandraSourceConfiguration extends AbstractCassandraConfiguration {
#Autowired
protected Environment environment;
#Override
protected int getPort() {
return Integer.parseInt(environment.getProperty("cassandra.port"));
}
#Override
protected String getContactPoints() {
return environment.getProperty("cassandra.contactpoints");
}
#Override
protected String getLocalDataCenter() {
return environment.getProperty("cassandra.data.center",
"datacenter1");
}
#Override
protected abstract String getKeyspaceName();
}
#Configuration
#EnableCassandraRepositories(basePackages = "com.data.cassandra.primary.repository",
repositoryBaseClass = CassandraRepositoryWithTtlWithTimestampImpl.class)
public class PrimaryCassandraSourceConfiguration extends AbstractCassandraSourceConfiguration {
#Override
protected String getKeyspaceName() {
return "primary_keyspace";
}
#Override
#Bean(name = "cassandraSession")
#Primary
public CqlSessionFactoryBean cassandraSession() {
final CqlSessionFactoryBean session = super.cassandraSession();
session.setKeyspaceName(getKeyspaceName());
return session;
}
#Bean(name = "cassandraTemplate")
public CassandraAdminOperations cassandraTemplate(
#Qualifier("cassandraSession") final CqlSessionFactoryBean session) {
return new CassandraAdminTemplate(session.getObject(),
cassandraConverter());
}
}
#Configuration
#EnableCassandraRepositories(cassandraTemplateRef = "secondaryCassandraTemplate",
basePackages = "com.data.cassandra.secondary.repository",
repositoryBaseClass = CassandraRepositoryWithTtlWithTimestampImpl.class)
public class SecondaryCassandraSourceConfiguration extends AbstractCassandraSourceConfiguration {
#Override
protected String getKeyspaceName() {
return "secondary_keyspace";
}
#Override
#Bean(name = "secondaryCassandraSession")
public CqlSessionFactoryBean cassandraSession() {
final CqlSessionFactoryBean session = super.cassandraSession();
session.setKeyspaceName(getKeyspaceName());
return session;
}
#Bean(name = "secondaryCassandraTemplate")
public CassandraAdminOperations cassandraTemplate(
#Qualifier("secondaryCassandraSession") final CqlSessionFactoryBean session) {
return new CassandraAdminTemplate(session.getObject(),
cassandraConverter());
}
}
I'm using spring-data-cassandra=3.0.4.RELEASE.
In my project I needed to connect to 2 keyspaces from 1 DC each. What I've configured in Cassandra #Configuration Bean is 1 Bean of Session, SessionFactoryFactoryBean and CassandraOperations per keyspace.

Spring batch + repository Testing

This is my reader that work in job and step i'u using a repository to get users(public interface UserRepository extends JpaRepository<User,Long>):
#Slf4j
public class Reader implements ItemReader<User> {
#Autowired
UserRepository userRepository;
private Iterator<User>userIterator;
#BeforeStep
public void before(StepExecution execution){
userIterator=userRepository.findAll().iterator();
}
#Override
public User read() {
if (userIterator != null && userIterator.hasNext()) {
User user=userIterator.next();
log.info("User-->"+user.toString());
return user;
} else {
return null;
}
}
}
This is my test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringBatchApplication.class, BatchTestConfiguration.class})
public class SpringBatchApplicationTest {
#Autowired
private JobLauncherTestUtils testUtils;
#Autowired
private BatchConfig config;
#Test
public void testEntireJob() throws Exception {
final JobExecution result = testUtils.getJobLauncher().run(config.processJob(), testUtils.getUniqueJobParameters());
Assert.assertNotNull(result);
Assert.assertEquals(BatchStatus.COMPLETED, result.getStatus());
}
#Test
public void testSpecificStep() {
Assert.assertEquals(BatchStatus.COMPLETED, testUtils.launchStep("orderStep1").getStatus());
}
}
When i`m running my test i got a :
Caused by: java.lang.IllegalStateException: Cannot determine embedded database for tests. If you want an embedded database please put a supported one on the classpath.
What do i need to add to make determine of my database. Do i need to place application properties somewhere or something else?
There is how my test situate in project enter image description here

Why do I have a circular dependency through SessionFactory in Spring-boot application?

I faced an error while launching the Spring-Boot application.
Now, I want to use HibernateDaoSupport in the DAO repository as Spring boot doesn't create SessionFactory automatically. So, I have created SessionFactory bean from EntityManagerFactory and tried to autowire it in the DAO class.
But I got the follow error :
Description:
The dependencies of some of the beans in the application context form a cycle:
fooDao defined in file [/home/user/test/out/production/classes/by/test/testing_the_java_service_layer/repository/FooDao.class]
┌─────┐
| sessionFactory defined in class path resource [by/test/testing_the_java_service_layer/configuration/Config.class]
└─────┘
I don't understand why SessionFactory is referring to FooDao class.
Following are the code samples :
FooDao.java
#Repository
public class FooDao extends HibernateDaoSupport
{
#Autowired
public void setSessionFactories( SessionFactory sessionFactory )
{
setSessionFactory( sessionFactory );
}
#Transactional
public int create( Foo entity )
{
return (int) this.getHibernateTemplate().save( entity );
}
}
Config.java
#ComponentScan( basePackages = { "by.test" } )
#Configuration
public class Config
{
/*
* Spring boot doesn't create SessionFactory bean, so we have to create it manually, using EntityManagerFactory
*/
#Bean
public SessionFactory sessionFactory( EntityManagerFactory entityManagerFactory )
{
return entityManagerFactory.unwrap( SessionFactory.class );
}
}
Foo.java
#Entity
#Table( name = "bibis" )
public class Foo
{
#Id
#Column( name = "foo", nullable = false )
public int foo;
#Column( name = "bar" )
public String bar;
}
TestApplication.java
#SpringBootApplication
public class TestApplication
{
public static void main( String[] args )
{
SpringApplication.run( TestApplication.class, args );
}
}
application.yaml
spring:
datasource:
username: 'bibis'
password: 'bibis'
schema: 'bibis'
host: 'localhost:3306'
url: 'jdbc:mariadb://localhost:3306/bibis'
and gradle dependencies from build.gradle
implementation('org.mariadb.jdbc:mariadb-java-client')
developmentOnly 'org.springframework.boot:spring-boot-devtools'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.1.8.RELEASE'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
I tried to use #Lazy annotation, constructor or field injection but it didn't help.
I have referred the answer from Spring Boot - Handle to Hibernate SessionFactory
Return HibernateJpaSessionFactoryBean obj instead of SessionFactory object from Config class.
The modified code will be :
#ComponentScan( basePackages = { "by.test" } )
#Configuration
public class Config
{
#Bean
public HibernateJpaSessionFactoryBean sessionFactory(EntityManagerFactory entityManagerFactory)
{
HibernateJpaSessionFactoryBean hibernateJpaSessionFactoryBean = new HibernateJpaSessionFactoryBean();
hibernateJpaSessionFactoryBean.setEntityManagerFactory(entityManagerFactory);
return hibernateJpaSessionFactoryBean;
}
}
Or you can do :
#Repository
public class FooDao extends HibernateDaoSupport
{
#Autowired
public void setSessionFactories(EntityManagerFactory entityManagerFactory)
{
setSessionFactory(entityManagerFactory.unwrap(SessionFactory.class));
}
#Transactional
public int create(Foo entity)
{
return (int) this.getHibernateTemplate().save( entity );
}
}
Appear to me that when you're using the #Autowired in the setter value, it's creating the circle dependency. It's a better approach that might solve your problem to do this as a property constructor
#Repository
public class FooDao extends HibernateDaoSupport
{
#Autowired
public FooDao(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory
}
#Transactional
public int create( Foo entity )
{
return (int) this.getHibernateTemplate().save( entity );
}
}
Well, you have to clarify if you are going to use JPA or Hibernate (implementation of JPA), EntityManager corresponds to JPA and SessionFactory to Hibernate, if you use EntityManager no need to use SessionFactory,
the EM invokes the hibernate session under the hood. And if you need some specific features that are not available in the EntityManager, you can obtain the session by calling:
Session session = entityManager.unwrap(Session.class);
refactor that and try

Springboot: Required a bean of type 'javax.persistence.EntityManagerFactory' that could not be found

I am in the process of developing a Spring Boot application and came across this error when starting the server. I am not sure if I am incorrectly defining any annotations or missing any dependencies. Any help would be appreciated.
Main class:
#SpringBootApplication
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class DemoApplication {
public static void main(String[] args) {
System.out.println("Hello world");
SpringApplication.run(DemoApplication.class, args);
}
}
UserService class:
#Service
public class UserService implements IUserService {
#Autowired
private UserDAO userDAO;
#Override
public List<UserDTO> getAllUsers() {
List<User> entities = userDAO.getUsers();
List<UserDTO> users = new ArrayList<UserDTO>();//Will never use directly User Object, will be using Tranferable objects
Iterator<User> iterator = entities.iterator();
while(iterator.hasNext()) {
User user = iterator.next();
users.add(ApiDTOBuilder.userToUserDTO(user));//We are building UserDTO object.
}
return users;
}
#Override
public UserDTO getUserByUsername(String username) {
User user = userDAO.getUser(username);
return ApiDTOBuilder.userToUserDTO(user);
}
#Override
public void createUser(UserDTO user) {
userDAO.createUser(ApiDTOBuilder.userDTOToUser(user));
}
#Override
public void updateUser(UserDTO user) {
userDAO.updateUser(ApiDTOBuilder.userDTOToUser(user));
}
#Override
public void deleteUser(String username) {
userDAO.deleteUser(username);
}
}
UserDAO class:
#Repository
public class UserDAO implements IUserDAO {
#PersistenceContext
EntityManager em;//JPA EntityManager is used to access a database in a particular application. It is used to manage persistent entity instances, to find entities by their primary key identity(As here is Username), and to query over all entities.
#Override
#Transactional
public User getUser(String username) {
return em.find(User.class, username); //Here entitymanager can find the username as it has the capability to find an entity by unique identifies
}
#Override
#Transactional
public List<User> getUsers() {
List<User> resultList = em.createQuery("FROM user_tbl", User.class).getResultList();
return resultList;
}
#Override
#Transactional
public void createUser(User user) {
em.persist(user);//Make an instance managed and persistent.
}
#Override
#Transactional
public void updateUser(User user) {
em.merge(user);//Merge the state of the given entity into the current persistence context.
}
#Override
#Transactional
public void deleteUser(String username) {
User user = this.getUser(username);
em.remove(user);//Remove the entity instance.
}
}
Build.gradle:
buildscript {
ext {
springBootVersion = '2.0.0.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final')
compile('javax.xml.bind:jaxb-api:2.2.11')
compile('org.springframework.boot:spring-boot-starter-test:2.0.2.RELEASE')
compile('com.sun.xml.bind:jaxb-core:2.2.11')
compile('com.sun.xml.bind:jaxb-impl:2.2.11')
runtime('org.springframework.boot:spring-boot-devtools')
runtime('mysql:mysql-connector-java')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Error message:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field userDAO in com.example.demo.service.UserService required a bean of type 'javax.persistence.EntityManagerFactory' that could not be found.
Action:
Consider defining a bean of type 'javax.persistence.EntityManagerFactory' in your configuration.
I saw all the relevant answers and tried to implement those suggestions like adding dependencies, adding notations in the main class, etc. But it shows the same error. Please someone help me out. Thank you in advance.
P.S: Github link of my application: https://github.com/heliOpenxCell/demo
i got the same your problem, just add this to your maven or gradle, it work for me!
Maven:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>9.0.10</version>
</dependency>
Gradle:
compile group: 'org.apache.tomcat', name: 'tomcat-jdbc', version: '9.0.10'

Spring Boot integration mybatis annotations error

When I run the following program I receive the following error. How can I solve it?
Description:
file [E:\program\github\demo\target\classes\com\example\demo\mapper\UserMapper.class] required a single bean, but 4 were found:
- &userMapper: defined in file [E:\program\github\demo\target\classes\com\example\demo\mapper\UserMapper.class]
- systemEnvironment: a programmatically registered singleton - contextParameters: a programmatically registered singleton - contextAttributes: a programmatically registered singleton
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Disconnected from the target VM, address: '127.0.0.1:58011', transport: 'socket'
Process finished with exit code 1
Here is my Spring Boot config file application.properties:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
spring.datasource.username=root
spring.datasource.password=123456
mybatis.type-aliases-package=com.example.demo.entity
DemoApplication.java -- bootstrap class
#SpringBootApplication
#EnableTransactionManagement
#MapperScan(value="com.example.demo.mapper")
public class DemoApplication {
#Autowired
private UserService userService;
#RequestMapping("add")
public void add(User user) {
userService.addUser(user);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
entity -- User.java
public class User implements Serializable {
private int userId;
private String username;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
'}';
}
}
Service -- UserService.java
public interface UserService {
void addUser(User user);
}
Service implementation -- UserServiceImpl.java
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserMapper userMapper;
#Override
#Transactional
public void addUser(User user) {
userMapper.insert(user);
}
}
Mybatis interface -- UserMapper.java
#Mapper
public interface UserMapper {
#Insert(
value = "insert into user values(#{user.userId},#{user.username})"
)
void insert(User user);
}

Resources