Update Logic for JPA Entity Fails when using DBUnit and Spring - spring

I am currently using DBUnit in conjunction with Spring in order to unit test my application, but have run into an issue where my update logic test always fails because a deadlock occurs on the database and I cannot figure out why this is the case. Please note that I have been able to get around the issue by removing the method annotated by #After, which really isn't needed because I am using the #TransactionConfiguration annotation, but I'm concerned that I'm misunderstanding something regarding how the transaction processing works and thus am hoping someone can indicate why I always get the following exception when running my updateTerritory method.
java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested
One thing that may be helpful to point out is that I am able to perform other actions like querying the database and inserting new records without any lock errors. In addition I am using OpenJPA and spring is injecting the PersistenceUnit into my DAO. I'm guessing that mixing up the usage of the PersistenceUnit and the direct use of the datasource within my DBUnit setup code (testSetup and testTeardown) may be part of the issue. I'm currently using Derby as my database.
My Code is provided below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/applicationContext.xml")
#TransactionConfiguration(defaultRollback = true)
public class TerritoryZoneManagerTest {
#Autowired
private DataSource unitTestingDataSource;
#Autowired
private ITerritoryZoneDaoManager mgr;
#Before
public void testSetup() throws DatabaseUnitException, SQLException,
FileNotFoundException {
Connection con = DataSourceUtils.getConnection(unitTestingDataSource);
IDatabaseConnection dbUnitCon = new DatabaseConnection(con);
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
IDataSet dataSet = builder
.build(new FileInputStream(
"./src/com.company.territoryzonelookup/dao/test/TerritoryZoneManagerTest.xml"));
try {
// NOTE: There is no need to use the DatabaseOperation.DELETE
// functionality because spring will automatically remove all
// inserted records after each test case is executed.
DatabaseOperation.REFRESH.execute(dbUnitCon, dataSet);
} finally {
DataSourceUtils.releaseConnection(con, unitTestingDataSource);
}
}
#After
public void testTeardown() throws DatabaseUnitException, SQLException,
FileNotFoundException {
Connection con = DataSourceUtils.getConnection(unitTestingDataSource);
IDatabaseConnection dbUnitCon = new DatabaseConnection(con);
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
IDataSet dataSet = builder
.build(new FileInputStream(
"./src/com.company.territoryzonelookup/dao/test/TerritoryZoneManagerTest.xml"));
try {
// NOTE: There is no need to use the DatabaseOperation.DELETE
// functionality because spring will automatically remove all
// inserted records after each test case is executed.
DatabaseOperation.DELETE.execute(dbUnitCon, dataSet);
} finally {
DataSourceUtils.releaseConnection(con, unitTestingDataSource);
}
}
#Test
#Transactional
public void updateTerritory() {
TerritoryZone zone = new TerritoryZone();
int id = 1;
zone = mgr.getTerritory(id);
String newCity = "Congerville";
zone.setCity(newCity);
mgr.updateTerritory(zone);
zone = mgr.getTerritory(id);
Assert.assertEquals(newCity, zone.getCity());
}
}
The DAO object is provided below as well in case that is useful.
#Repository
public class TerritoryZoneDaoManager implements ITerritoryZoneDaoManager {
/*
#Autowired
private EntityManagerFactory emf;
*/
/*
* #PersistenceUnit EntityManagerFactory emf;
*
* #PersistenceContext private EntityManager getEntityManager(){ return
* emf.createEntityManager(); }
*/
#PersistenceContext
private EntityManager em;
private EntityManager getEntityManager() {
// return emf.createEntityManager();
return em;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#addTerritory(com.company.territoryzonelookup.dao.TerritoryZone)
*/
#Override
public TerritoryZone addTerritory(TerritoryZone territoryZone) {
EntityManager em = getEntityManager();
em.persist(territoryZone);
return territoryZone;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#getTerritory(int)
*/
#Override
public TerritoryZone getTerritory(int id) {
TerritoryZone obj = null;
Query query = getEntityManager().createNamedQuery("selectById");
query.setParameter("id", id);
obj = (TerritoryZone) query.getSingleResult();
return obj;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#updateTerritory(com.company.territoryzonelookup.dao.TerritoryZone)
*/
#Override
public TerritoryZone updateTerritory(TerritoryZone territoryZone){
getEntityManager().merge(territoryZone);
return territoryZone;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#getActiveTerritoriesByStateZipLob(java.lang.String, java.lang.String, java.util.Date, java.lang.String)
*/
#Override
public List<TerritoryZone> getActiveTerritoriesByStateZipLob(String stateCd, String zipCode, Date effectiveDate, String lobCd){
List<TerritoryZone> territoryList;
Query query = getEntityManager().createNamedQuery("selectActiveByZipStateLob");
query.setParameter("zipCode", zipCode);
query.setParameter("state", stateCd);
query.setParameter("lob",lobCd);
query.setParameter("effectiveDate", effectiveDate);
territoryList = (List<TerritoryZone>) query.getResultList();
return territoryList;
}
/* (non-Javadoc)
* #see com.company.territoryzonelookup.dao.ITerritoryZoneManager#deleteAll()
*/
#Override
public void deleteAll(){
Query query = getEntityManager().createNativeQuery("Delete from TerritoryZone");
query.executeUpdate();
}
/***
* the load method will remove all existing records from the database and then will reload it using it the data passed.
* #param terrList
*/
public void load(List<TerritoryZone> terrList){
deleteAll();
for (TerritoryZone terr:terrList){
addTerritory(terr);
}
}
}
Thanks in advance for your assistance.
Jeremy

jwmajors81
i can not know what's wrong with your unit testing code for lacking some details.
i also used spring unit test and dbunit for my himvc framework, a RAD framework based on spring3 and hibernate. here is the code of my super class for unit testingļ¼Œ
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:config/application-test-config.xml"})
#Transactional
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class HiMVCTransactionalUnitTest extends AbstractTransactionalJUnit4SpringContextTests{
#Autowired
protected DBUnitHelper dbHelper;
protected void loadFixture(){
try{
String fixtureFile=this.dbHelper.getDataFile();
if(fixtureFile==null){
fixtureFile=this.getDefaultXMLFixtureFile();
this.dbHelper.setDataFile(fixtureFile);
}
if(this.dbHelper.isDataFileExisted()){
if(this.dbHelper.isMSSQL()){
HiMVCInsertIdentityOperation operation=new HiMVCInsertIdentityOperation(DatabaseOperation.CLEAN_INSERT);
operation.setInTransaction(true);
this.dbHelper.executeDBOperation(operation);
}else{
this.dbHelper.executeDBOperation(DatabaseOperation.CLEAN_INSERT);
}
}
}catch(Exception x){
x.printStackTrace();
}
}
...
}
i use the #Transactional annotation in the class declaration, and also specified the transactionManager. i wrote a DBUnitHelper to wrap the dbunit details of data loading.
here is a unit test sample:
public class MyTest extends HiMVCTransactionalUnitTest{
#Before
public void setup(){
super.loadFixture();
}
//other testing methods
}
Hope these codes helpful.

Related

Get Entity Class in Generic Spring Data Repository Fragment

I'm trying to add some custom functionality to a Spring Data Repository using the Repository composing features documented here
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.single-repository-behavior
Specifically, I'm trying to create a generic interface and implementation I can use with many of my repositories which seem to be a valid approach given "Example 34. Fragments overriding" in the link above which shows just this scenario.
I have got my generic interface DynamicEntityGraph
public interface DynamicEntityGraph<T> {
List<T> findAll(EntityGraph<T> entityGraph);
}
and the corresponding implementation
public class DynamicEntityGraphImpl<T> implements DynamicEntityGraph<T> {
#Autowired
private EntityManager em;
Logger logger = LoggerFactory.getLogger(getClass());
#Override
public List<T> findAll(EntityGraph<T> entityGraph) {
//EM Code Goes Here
return null;
}
}
which I then use in a repository like
#Repository
public interface SiteRepository extends CustomerScopedRepository<Site>, DynamicEntityGraph<Site> {
}
This part all works fine.
My issue is in the DynamicEntityGraphImpl I need to get the Class of T at runtime in order to create my Entity Manager queries. After extensive research, I've not been able to find a solution in which I can obtain the Class of T, despite it looking like a valid solution given it exists in the Spring Data JPA Docs.
I've read the source code of JpaRepositoryFactory and can see the class is injected into the repository when it's constructed, but I'm not sure how to access this from the fragment.
Any assistance would be greatly appreciated.
For my future self or other lost travellers on a similar path, I figured it out and here is the solution.
A similar question was asked on the Spring Data project and this clears up that fragments are actually initialised as singletons in the Spring context so it's not possible to pass the EntityInformation to them.
Note: If you're not trying to make a generic fragment then this isn't the right solution for you.
The solution is to then extend the JpaRepositoryFactory and JpaRepositoryFactoryBean with a little extra configuration.
public class MyJpaRepositoryFactory extends JpaRepositoryFactory {
private EntityManager entityManager;
public MyJpaRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
#Override
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
RepositoryFragments fragments = super.getRepositoryFragments(metadata);
if(DynamicEntityGraph.class.isAssignableFrom(metadata.getRepositoryInterface()))
{
JpaEntityInformation<?, Serializable> entityInformation = this.getEntityInformation(metadata.getDomainType());
fragments = fragments.append(RepositoryFragment.implemented(DynamicEntityGraph.class, new DynamicEntityGraphImpl(entityInformation, entityManager)));
}
return fragments;
}
}
this simply checks if the repository being instantiated implements our interface and if it does we construct a new instance of our class, passing it the EntityInformation and EntityManger.
public class MyJpaRepositoryFactoryBean<T extends JpaRepository<Object, Serializable>> extends JpaRepositoryFactoryBean<T , Object, Serializable> {
public MyJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
#Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyJpaRepositoryFactory(entityManager);
}
}
and finally, we tell Spring Data to use this as our factory bean
#SpringBootApplication
#EnableJpaRepositories(repositoryFactoryBeanClass = MyJpaRepositoryFactoryBean.class)
public class ApplicationApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApplicationApiApplication.class, args);
}
}
It's important to also mark your fragment interface as #NoRepositoryBean to prevent Spring from making a bean from it.
#NoRepositoryBean
public interface DynamicEntityGraph<T> {
List<T> findAll(EntityGraph<T> entityGraph);
}
I hope this saves you some time!
I am using a utility object SubGraphUtil. SubGraphUtil is used to build the graph.
I also have a specification object for the where part
public abstract class AbstractCustomRepository<T> {
private static final String FETCH_GRAPH = "javax.persistence.fetchgraph";
#PersistenceContext
protected EntityManager entityManager;
#Setter
private Class<T> entityClass;
/**
* Permet de recuperer une liste d entites en choisissant le graphe et la specification
*
* #param subGraphUtilList
* #param specification
* #return une liste d entites en choisissant le graphe
*/
public List<T> findAllWithGraphAndSpecification(List<SubGraphUtil> subGraphUtilList,
#Nullable Specification<T> specification) {
EntityGraph<T> graph = buildEntityGraphFromUtil(subGraphUtilList);
CriteriaQuery<T> criteriaQuery = getCriteriaQuery(specification);
return entityManager.createQuery(criteriaQuery)
.setHint(FETCH_GRAPH, graph)
.getResultList();
}
/**
* Permet de construire EntityGraph
*
* #param subGraphUtilList
* #return EntityGraph
*/
private EntityGraph<T> buildEntityGraphFromUtil(List<SubGraphUtil> subGraphUtilList) {
EntityGraph<T> graph = entityManager.createEntityGraph(entityClass);
if (isNotEmpty(subGraphUtilList)) {
subGraphUtilList.forEach(
subGraphUtil -> {
Subgraph<T> subgraph = graph.addSubgraph(subGraphUtil.getName());
if (isNotEmpty(subGraphUtil.getSubGraphUtilList())) {
subGraphUtil.getSubGraphUtilList()
.forEach(
childGraph -> addSubGraph(subgraph, childGraph)
);
}
}
);
}
return graph;
}
}
Example of use in a repository
public class ContratCustomRepositoryImpl extends AbstractCustomRepository<Contrat>
implements ContratCustomRepository {
public ContratCustomRepositoryImpl() {
super();
setEntityClass(Contrat.class);
}
}
public interface ContratCustomRepository {
List<Contrat> findAllWithGraphAndSpecification(List<SubGraphUtil> subGraphUtilList,
Specification<Contrat> specification);
}
Full code: Here

Spring-data JdbcTemplate does not commit

I need to update thousands of records in the database but i would like to commit after a batch of 5000 records.
#Service
#Transactional (rollbackFor=Throwable.class)
public class AttributeProcessorServiceImpl extends DataLoader implements
AttributeProcessorService
{
.....
private final TransactionTemplate transTemplate;
private final JdbcTemplate jdbcTemplate;
#Autowired private PlatformTransactionManager platformTransactionManager;
#Autowired
public BlockAttributeProcessorServiceImpl(
TransactionTemplate transTemplate,
JdbcTemplate jdbcTemplate,
.....)
{
super();
this.transTemplate = transTemplate;
this.jdbcTemplate=jdbcTemplate;
.....
}
#Async
#Transactional (propagation=Propagation.NOT_SUPPORTED)
public void reloadAttrs()
{
loadAttrs();
updateAttrs();
}
private void loadAttrs()
{
...some data fetching and processing, finally call db update.
updateDbInBatches(rowcount, sql);
}
private void updateAttrs()
{
...some data fetching and processing, finally call db update.
updateDbInBatches(rowcount, sql);
}
private void updateDbInBatches(long rowcount, String sql)
{
DefaultTransactionDefinition def;
boolean hasMore=true;
Integer from;
Integer to = 0;
int batchSize=5000; //gets from property
while (hasMore)
{
from = to+1;
to = batchSize;
def = new DefaultTransactionDefinition();
def.setName("backCommitTx");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = platformTransactionManager.getTransaction(def);
int rows = jdbcTemplate.update(sql,paramValues,paramTypes);
logger.debug("Loaded ["+rows+"] records.");
platformTransactionManager.commit(status);
if (to > rowcount)
{
hasMore=false;
logger.debug("All records ["+rowcount+"] updated.");
}
}
}
}
If I put a breakpoint after loadAttrs(), it shows it loaded bunch of records to the database and issued a commit(), but database does not reflect that commit, until after entire public method completes. How do i ensure data is indeed written to the database after each commit. commit neither gives any error as well.
I missed an important piece of information that solved the problem.
I had another public method which is what was called from outside.
public void reloadAttrs(TransDetail trans)
{
reloadAttrs();
}
Above method was infact using default Transaction Propagation as i did not mention it specifically. Since this was the first public method that was called, spring was ignoring transaction demarcation on next public (async) method that was called. I changed above signature to:
#Transactional (propagation=Propagation.NOT_SUPPORTED)
public void reloadAttrs(TransDetail trans)
{
reloadAttrs();
}
It then worked. I was able to see changes in the database after every commit.

Spring + Hibernate + TestNG + Mocking nothing persist, nothing is readed in test

Fighting with TestNG, Spring an Hibernate. I'm writing test for Service class, and it's always failure. But without test class works fine. So App is working, but tests don't want to.
Here is my test class
#Transactional
public class BorrowerServiceTest {
#Mock
BorrowerDAOImpl borrowerDAO;
#InjectMocks
BorrowerService borrowerService;
#BeforeClass
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void persistTest() {
Borrower borrower = new Borrower.BorrowerBuilder().firstName("Lars").lastName("Urlich").adress("LA")
.phoneNumber("900900990").build();
borrowerService.persist(borrower);
List<Borrower> borrowerList = borrowerService.getBorrowerByName("Lars Urlich");
Assert.assertEquals(true, borrower.equals(borrowerList.get(0)));
}
}
My BorrowerService:
#Service("borrowerService")
#Transactional
public class BorrowerService {
#Autowired
private BorrowerDAO borrowerDAO;
public List<Borrower> getBorrowers() {
return borrowerDAO.getBorrowers();
}
public List<Borrower> getBorrowerByName(String name) {
return borrowerDAO.getBorrowerByName(name);
}
public boolean removeBorrower(Borrower borrower) {
return borrowerDAO.removeBorrower(borrower);
}
public boolean persist(Borrower borrower) {
return borrowerDAO.persist(borrower);
}
}
My BorrowerDAOImpl:
#Repository("borrowerDAO")
#Transactional
public class BorrowerDAOImpl extends DAO implements BorrowerDAO {
#Override
public List<Borrower> getBorrowers() {
List<Borrower> borrowerList = null;
Query query = entityManager.createQuery("SELECT B FROM Borrower B");
borrowerList = query.getResultList();
return borrowerList;
}
#Override
public List<Borrower> getBorrowerByName(String name) {
List<Borrower> borrowerList = null;
String[] values = name.split(" ");
Query query = entityManager.createQuery("SELECT B FROM Borrower B WHERE B.firstName LIKE '" + values[0]
+ "' AND B.lastName LIKE '" + values[1] + "'");
borrowerList = query.getResultList();
return borrowerList;
}
#Override
public boolean removeBorrower(Borrower borrower) {
String firstName = borrower.getFirstName();
String lastName = borrower.getLastName();
Query query = entityManager
.createQuery("DELETE Borrower where FIRST_NAME LIKE :FirstName AND LAST_NAME LIKE :LastName");
query.setParameter("FirstName", firstName);
query.setParameter("LastName", lastName);
query.executeUpdate();
return true;
}
#Override
public boolean persist(Borrower borrower) {
entityManager.persist(borrower);
return true;
}
}
and abstract DAO:
#Repository
#Transactional
public abstract class DAO {
#PersistenceContext
protected EntityManager entityManager;
}
Maven returns failure:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.LinkedList.checkElementIndex(LinkedList.java:555)
at java.util.LinkedList.get(LinkedList.java:476)
at com.me.service.test.BorrowerServiceTest.persistTest(BorrowerServiceTest.java:41)
I also had to fight with this. The problem here is that your test runs in it's own transaction, so nothing will be committed during method's execution. Now here is what I did:
public class IntegrationTest extends SomeTestBase
{
#Autowired
private PlatformTransactionManager platformTransactionManager;
private TransactionTemplate transactionTemplate;
#Autowired
private BeanToTest beanToTest;
#Override
#Before
public void setup()
{
super.setup();
this.transactionTemplate = new TransactionTemplate(this.platformTransactionManager);
}
#Test
public void fooTest()
{
// given
// when
boolean result = this.transactionTemplate.execute(new TransactionCallback<Boolean>()
{
#Override
public Boolean doInTransaction(TransactionStatus status)
{
return IntegrationTest.this.beanToTest.foo();
}
});
// then
}
}
This allows you to have methods execute within a separate transaction. Please note that you might declare some variables as final.
Hope that helps.
Check the Spring documentation: it looks your test class should extend AbstractTestNGSpringContextTests.
Use #Commit annotation on the whole test class or even method to persist changes made in the test. For more information https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#commit

Spring #Transactional propagation effect of REQUIRES_NEW?

I am doing some tests to understand the behaviour of #Transactional in Spring 3. Though, it is not working as I would expect. If have one method with Propagation.REQUIRED calling another with Propagation.REQUIRES_NEW, will the second method be able to retrieve from the DB the data inserted by the first method?
EDITED:
I AM seeing uncommitted changed in a #Transaction, here is my (nasty looking) code.
#Service
public class FeedManager {
#Autowired
JdbcTemplate jdbcTemplate;
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
public boolean createFeed(Feed feed, boolean anonymizeIt) {
String query = "INSERT INTO feed (name, url, is_active) values (?, ?, ?)";
int rowsAffected = jdbcTemplate.update(query, feed.getName(), feed.getUrl(), feed.isActive());
boolean success = (rowsAffected == 1);
if (anonymizeIt) {
success = success && this.anonymizeFeedName(feed);
}
return success;
}
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public boolean anonymizeFeedName(Feed feed) {
String query = "UPDATE feed set name = ? where name = ?";
int rowsAffected = jdbcTemplate.update(query, feed.getName() + (new Date()).toString(), feed.getName());
boolean success = (rowsAffected == 1);
return success;
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:mrpomario/springcore/jdbc/jdbc-testenv-config.xml")
public class TransactionalTest {
#Autowired
FeedManager feedManager;
Feed feed;
#Before
public void setup() {
feed = new Feed("RSS", "http://www.feedlink.com", true);
}
#Test
public void test_Create() {
assertTrue(feedManager.createFeed(feed, false));
}
#Test
public void test_Anonymize() {
assertTrue(feedManager.anonymizeFeedName(feed));
}
#Test
public void test_Create_And_Anonymize() {
Feed feedo = new Feed("AnotherRSS", "http://www.anotherfeedlink.com", true);
assertTrue(feedManager.createFeed(feedo, true));
}
}
It should not be able to see any changes made by the first method (as long as your isolation level is READ COMMITTED or above).
If you get different results, make sure that #Transactional actually takes effect. In particular, make sure that you don't call another #Transactional method of the same class - due to limitations of Spring proxy-based AOP model transactional aspect is applied only to calls that come from the outside of the class.
See also:
7.6.1 Understanding AOP proxies

org.neo4j.graphdb.NotInTransactionException

Its been a week banging my head over this but still cannot seem to find a solution. I am using spring-data-neo4j maven artifact and the following lines of code which causes this issue:
/**
*
*/
#Autowired
private UserRepository userRepository;
#Transactional
public void addClassDescriptor(User user, ClassDescriptor classDescriptor) {
Project project = user.getDefaultProject();
ManagedFieldAccessorSet<ClassDescriptor> accessorSet = (ManagedFieldAccessorSet<ClassDescriptor>) project.getClassDescriptors();
accessorSet.add(classDescriptor);
/*
* Save the user object after updating the set
*/
userRepository.save(user);
}
When executing the method it gives the following error at
accessorSet.add(classDescriptor);
Stacktrace:
org.neo4j.graphdb.NotInTransactionException
at org.neo4j.kernel.impl.persistence.PersistenceManager.getResource(PersistenceManager.java:252)
at org.neo4j.kernel.impl.persistence.PersistenceManager.nodeCreate(PersistenceManager.java:155)
at org.neo4j.kernel.impl.core.NodeManager.createNode(NodeManager.java:270)
at org.neo4j.kernel.EmbeddedGraphDbImpl.createNode(EmbeddedGraphDbImpl.java:317)
at org.neo4j.kernel.EmbeddedGraphDatabase.createNode(EmbeddedGraphDatabase.java:103)
at org.springframework.data.neo4j.support.DelegatingGraphDatabase.createNode(DelegatingGraphDatabase.java:82)
at org.springframework.data.neo4j.support.mapping.EntityStateHandler.useOrCreateState(EntityStateHandler.java:115)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.write(Neo4jEntityConverterImpl.java:145)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.write(Neo4jEntityPersister.java:176)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:238)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:227)
at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:295)
at org.springframework.data.neo4j.fieldaccess.AbstractNodeRelationshipFieldAccessor.getOrCreateState(AbstractNodeRelationshipFieldAccessor.java:97)
at org.springframework.data.neo4j.fieldaccess.AbstractNodeRelationshipFieldAccessor.createSetOfTargetNodes(AbstractNodeRelationshipFieldAccessor.java:89)
at org.springframework.data.neo4j.fieldaccess.OneToNRelationshipFieldAccessorFactory$OneToNRelationshipFieldAccessor.setValue(OneToNRelationshipFieldAccessorFactory.java:66)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.updateValue(ManagedFieldAccessorSet.java:90)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.update(ManagedFieldAccessorSet.java:78)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.add(ManagedFieldAccessorSet.java:104)
My entities are as follows : ( User.java )
#GraphId
private Long id;
#RelatedTo(elementClass = Project.class)
#Fetch
private Set<Project> projects;
( Project.java )
#GraphId
private Long id;
/**
*
*/
#RelatedTo(elementClass = ClassDescriptor.class)
#Fetch
private Set<ClassDescriptor> classDescriptors;
/**
*
*/
private boolean defaultProject;
Please help ! Attached is the dependency tree.
I found when adding relations using a collection operation, as well as the #Transactional annotation you need to obtain a reference to the GraphDatabaseService and explicitly begin and end a transaction:
#Autowired
private GraphDatabaseService graphDb;
#Transactional
public void addRelation() {
Transaction tx = graphDb.beginTx();
...
tx.success(); //or tx.failure();
tx.finish();
}
Abhi,
If it is happening consistently then I think you service is not a proper bean. How do you wire up your stuff?
Are you using simple or advanced mode (AspectJ)?
The stuff you have posted looks fine and simple, so I can't see why it wouldn't work...
Regards,
Lasse
You must protect the Transaction in a try chach...
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
public class App {
static Node firstNode = null;
static GraphDatabaseService graphDb = null;
public static void main(String[] args) {
System.out.println("Start ...");
graphDb = ConnectNeo4j.initDB();
Transaction tx = graphDb.beginTx();
try {
firstNode = graphDb.createNode();
firstNode.setProperty("message", "Es Geht!");
System.out.println(firstNode.getProperty("message"));
tx.success();
} catch (Exception e) {
System.err.println(e.getMessage());
} finally {
tx.close();
}
}
}

Resources