Page 1 of 3 123 LastLast
Results 1 to 10 of 28

Thread: Applying transaction boundaries

  1. #1
    Join Date
    Oct 2011
    Posts
    27

    Default Applying transaction boundaries

    This kind of follows on from this this post here (http://forum.springsource.org/showth...ith-properties)

    What I am trying to do is have the @Service as my transaction boundary, but from what I can see the transaction boundary is actually being applied only at the repository layer.

    Given this service method:

    @Transactional
    public void addFriend(Person firstPerson, Person secondPerson) {
    firstPerson.makeFriendsWith(secondPerson);
    repository.save(firstPerson);
    }

    I end up getting a NotInTransactionException. I thought that my transaction manager wasn't being loaded at all but then I found a curious thing - when I commented out the makeFriendsWith method, I could run the the code without exception. Does this mean that the transaction boundary is not being properly applied to the service?

    FYI - inside the makeFriendsWith method, a new Friendship method is being created and added to the original Person's list of friends. Presumably this is kicking off a transaction which for some reason doesn't have an open transaction around it.

    Bug, or just coded wrong on my behalf?

    Thanks

  2. #2
    Join Date
    Jan 2011
    Location
    Dresden, Germany
    Posts
    525

    Default

    But you have tx management setup correctly ? And your @Service is injected by Spring?

    Can you perhaps share your project for us to check?

    Thanks Michael

  3. #3
    Join Date
    Oct 2011
    Posts
    27

    Default

    Thanks Michael,

    Just putting a tets up into GitHub now. The dependencies are almost the same as the hello world example, and my app context is as follows:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schem...-beans-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schem...ontext-3.0.xsd
    http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schem...-neo4j-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">


    <contextroperty-placeholder location="classpath:META-INF/socialsystem.properties"
    system-properties-mode="OVERRIDE"/>

    <context:annotation-config/>

    <context:spring-configured/>
    <context:component-scan base-package="gamesys.social"/>

    <neo4j:config graphDatabaseService="neo4jGraphDatabase"/>
    <neo4j:repositories base-package="gamesys.social.infrastructure.persistence .graph"/>

    <!--<bean id="graph" class="com.tinkerpop.blueprints.pgm.impls.neo4j.Ne o4jGraph">-->
    <!--<constructor-arg value="/tmp/graph.db"/>-->
    <!--</bean>-->

    <bean id="wrappingNeoServerBootstrapper" class="org.neo4j.server.WrappingNeoServerBootstrap per" init-method="start"
    destroy-method="stop">
    <constructor-arg ref="neo4jGraphDatabase"/>
    </bean>

    <bean id="neo4jGraphDatabase" class="${graph.graphClassName}"
    destroy-method="shutdown">
    <constructor-arg value="${graph.path}"/>
    <constructor-arg ref="neo4j-properties"/>
    </bean>

    <bean name="neo4j-properties" class="java.util.HashMap">
    <constructor-arg index="0">
    <map>
    <entry key="allow_store_upgrade">
    <value>true</value>
    </entry>
    <entry key="ha.machine_id" value="${server.id}"/>
    <entry key="ha.server" value="zoo1:${ha.server.port}"/>
    <entry key="ha.zoo_keeper_servers" value="${zookeeper.servers}"/>
    <entry key="enable_remote_shell" value="port=1331"/>
    <entry key="ha.pull_interval" value="1"/>
    </map>
    </constructor-arg>
    </bean>

    <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

    </beans>

    Not sure if perhaps my tx declaration is wrong but I'll upload the test now.

    Thanks

  4. #4
    Join Date
    Oct 2011
    Posts
    27

    Default

    Annoyingly I've written a bunch of unit tests which all work perfectly with my sample application on github, which makes what I am running into even weirder. I've put the github stuff in as I think it illustrates it.

    My service is at https://github.com/mikeycmccarthy/Sp...rviceImpl.java - We simply get or create two nodes, then relate them.

    The test for this is at https://github.com/mikeycmccarthy/Sp...rviceTest.java. It is super simple, and just tries to show I don't get a not in transaction exception.

    The domain model is at https://github.com/mikeycmccarthy/Sp...el/Member.java. Referring a member simply adds them to that list of referees.

    As I said, all this code passes, but in my actual work project, the process of performing the add inside the domain class (it's definitely this - I comment it out and all works) is what causes the exception. Is there any way that at this point we have somehow become detached from the wrapping transaction? And if so, what would cause this? My production code is almost identical to the gh stuff.

    My workaround for now I guess could be to use the template to save the relationship directly, but I'm still very confused as to how i have ended up outside of a transaction.

    Thanks

  5. #5
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,632

    Default

    Coded wrong. The 2 objects are retrieved outside of the transaction (in a test method in general there is a transaction around the test method making the 2 object retrieved inside the same transaction). The whole unit of work (getting the persons, adding friends) should be a single transaction. I suspect the calling code is first retrieving the persons then call this code.

    Also make sure that transactions are actually applied, in your stacktrace there should be a TransactionInterceptor somewhere.
    Last edited by Marten Deinum; Jan 26th, 2012 at 05:42 AM.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  6. #6
    Join Date
    Oct 2011
    Posts
    27

    Default

    I've tried to reproduce this in a project on GitHub and annoyingly it always seems to work there.

    I am confident everything is fine with my Spring setup (I've got tests that prove that a simple save works and a simple retrieve from the repository works).

    I am injecting the service into my test, much like this test here - https://github.com/mikeycmccarthy/Sp...rviceTest.java and I know for sure the transactionality being applied is from my service as my test class has no transactional annotations.

    I think the problem is that somehow my transaction seems to be missing when I do anything to the relationship. Consider my service code:

    @Override
    @Transactional
    public void refer(Long referer, Long referee) {
    Member refererMember = getOrCreateMember(referer);
    Member refereeMember = getOrCreateMember(referee);
    refererMember.refer(refereeMember); // Add the relationship
    memberRepository.save(refererMember);
    }

    public Member getOrCreateMember(long memberId) {

    Member member = memberRepository.findByPropertyValue("memberId", memberId);

    if (member == null) {
    member = memberRepository.save(new Member(memberId));
    }

    return member;
    }

    That commented line is the one that throws the NotInTransactionException (if it gets commented out the entities are persisted, just not the relationship). Interestingly, the exact same thing happens when I use the template directly to save the relationship (again, doesn't fail in my Github code, only on the production code).

    Are there any scenarios you can think of at all where the transaction is not available for persisting the relationship?

  7. #7
    Join Date
    Oct 2011
    Posts
    27

    Default

    If it helps, this is the particular stack trace exception at this point:

    org.neo4j.graphdb.NotInTransactionException
    at org.neo4j.kernel.impl.persistence.PersistenceManag er.getResource(PersistenceManager.java:253)
    at org.neo4j.kernel.impl.persistence.PersistenceManag er.relationshipCreate(PersistenceManager.java:162)
    at org.neo4j.kernel.impl.core.NodeManager.createRelat ionship(NodeManager.java:321)
    at org.neo4j.kernel.impl.core.NodeImpl.createRelation shipTo(NodeImpl.java:517)
    at org.neo4j.kernel.impl.core.NodeProxy.createRelatio nshipTo(NodeProxy.java:197)
    at org.springframework.data.neo4j.support.mapping.Ent ityStateHandler.createRelationship(EntityStateHand ler.java:131)
    at org.springframework.data.neo4j.support.mapping.Ent ityStateHandler.useOrCreateState(EntityStateHandle r.java:118)
    at org.springframework.data.neo4j.support.mapping.Neo 4jEntityConverterImpl.write(Neo4jEntityConverterIm pl.java:145)
    at org.springframework.data.neo4j.support.mapping.Neo 4jEntityPersister$CachedConverter.write(Neo4jEntit yPersister.java:176)
    at org.springframework.data.neo4j.support.mapping.Neo 4jEntityPersister.persist(Neo4jEntityPersister.jav a:244)
    at org.springframework.data.neo4j.support.mapping.Neo 4jEntityPersister.persist(Neo4jEntityPersister.jav a:227)
    at org.springframework.data.neo4j.support.Neo4jTempla te.save(Neo4jTemplate.java:287)
    at org.springframework.data.neo4j.fieldaccess.OneToNR elationshipEntityFieldAccessorFactory$OneToNRelati onshipEntityFieldAccessor.persistEntities(OneToNRe lationshipEntityFieldAccessorFactory.java:80)
    at org.springframework.data.neo4j.fieldaccess.OneToNR elationshipEntityFieldAccessorFactory$OneToNRelati onshipEntityFieldAccessor.setValue(OneToNRelations hipEntityFieldAccessorFactory.java:74)
    at org.springframework.data.neo4j.fieldaccess.Managed FieldAccessorSet.updateValue(ManagedFieldAccessorS et.java:90)
    at org.springframework.data.neo4j.fieldaccess.Managed FieldAccessorSet.update(ManagedFieldAccessorSet.ja va:78)
    at org.springframework.data.neo4j.fieldaccess.Managed FieldAccessorSet.add(ManagedFieldAccessorSet.java: 104)
    at xxx.social.model.xxx.makeBuddiesWith(xxx.java:58)

  8. #8
    Join Date
    Oct 2011
    Posts
    27

    Default

    FYI, I've actually found a slight difference - reading through the docs:

    It must not be a primitive type because then the "non-attached" case can not be represented as the default value 0 would point to the reference node. Please make also sure that an equals() and hashCode() method have to be provided which take the id field into account (and also handle the "non-attached", null case).

    I'm not factoring the graphId into hashCode and equals of my objects. I had purposely left this out but I'll add it in now with the recommendations. Can't imagine this is the fix, but it's a worthwhile thing to do anyways.

  9. #9
    Join Date
    Oct 2011
    Posts
    27

    Default

    Fixed - the test wasn't working properly because in the test context I was missing:

    <tx:annotation-driven mode="proxy"/>

    and the non test wasn't working because I defined the annotation mode in the graph bean as such:

    <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

  10. #10
    Join Date
    Jan 2012
    Posts
    9

    Default

    Hello Michael,

    I have the same issue... Using template.save() is working in a @Transactional @Test method, but not in a (non test) @Transactional @Service method. I had the <tx:annotation-driven mode="proxy"/> set up in all cases, as well as the annotation-config, spring-configured, component-scan, and neo4j config things.

    In the code below, as soon as the template.save() call is made, that NotInTransactionException is thrown.


    @Service
    public class SimpleDatabasePopulator {

    @Autowired
    Neo4jOperations template;

    @Transactional
    public void populateDatabase() {
    SimpleCustomer customer = template.save(new SimpleCustomer("A customer"));
    SimpleContact contact = template.save(new SimpleContact("A contact"));
    template.createRelationshipBetween(contact, customer, SimpleRelation.class, "CONTACT_OF_CUSTOMER", false);
    ...



    Anything else is needed to configure transactions? Did you have to do anything else that could fix this, like, do we really have to override equals and hashCode?

    Thank you,
    Daniel

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •