Announcement Announcement Module
Collapse
No announcement yet.
New data in transactions Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • New data in transactions

    I never remember when I have to call save() and when I can just let the commit of the transaction do all my work for me.

    So I have an Event which is already a node in the db. I want to add an ItemNeededForEvent. This also might require creating a brand new Item object. So the ItemNeededForEvent has an Event reference and an Item reference. When I find that the item is new, I create a new Item object (item) and create a new ItemNeededForEvent (itemNeeded) object. I set item in itemNeeded, then add itemNeeded to a collection in the Event object. I never call save. Should I be calling save on the new Item, on the new ItemNeededForEvent, or will it all be handled when I commit.

    Code in my EventServiceImpl class, all the methods in the public interface are all @Transactional.

    Look at the else portion below
    Code:
    public boolean createItemNeededForEvent(Long eventId,
                                             String itemName,
                                             String upc,
                                             BigDecimal quantity) {
            Event event = eventRepository.getEventById(eventId);
            Item item = findItem(upc, itemName);
            if (item != null) {
                event.addItemNeededForEvent(item, quantity);
                return true;
            } else {
                item = new Item(upc, itemName);
                event.addItemNeededForEvent(item, quantity);
                return true;
            }
        }
    here is the event method addItemNeededForEvent

    Code:
        public void addItemNeededForEvent(Item item, BigDecimal quantityNeeded) {
            ItemNeededForEvent itemNeeded = new ItemNeededForEvent(item, this, quantityNeeded);
            if (itemsNeededForEvent == null) {
                this.itemsNeededForEvent = new HashSet<ItemNeededForEvent>();
            }
            itemsNeededForEvent.add(itemNeeded);
        }
    Thanks

    Mark

  • #2
    Originally posted by bytor99999 View Post
    Should I be calling save on the new Item, on the new ItemNeededForEvent, or will it all be handled when I commit.
    Firstly, which mapping mode are you using - simple or advanced (AspectJ)?

    Assuming simple mapping mode, the tutorial gives an example: http://static.springsource.org/sprin...e/html/#d0e918 - yes, you call save on new entities.

    Full source here: https://github.com/SpringSource/spri...mples/cineasts

    Worth writing some tests in any case!

    Lasse

    Comment


    • #3
      Yes, using advanced. Yes, I have tests and they pass. And in the app, it doesn't exactly work as advertised. Or it is sporadic. Sometimes it saves, sometimes it doesn't.

      I think also the type of node that it is might also make a difference. Like a vertex node which has relation to three nodes, basically there just to hook the three nodes on one relationship, so to speak. If you create a new vertex and set all three relation nodes and add it to one of those three nodes collections, all within a transaction, that new vertex node is still not saved.

      So here is where I was getting intermittent saves with this code, which is not a vertex node. This is a Service method that is transactional. I put @Transactional on the class level, and yes the method is in the interface.

      Code:
      @Override
          public boolean createItemNeededForEvent(Long eventId,
                                               String itemName,
                                               String upc,
                                               BigDecimal quantity) {
              Event event = eventRepository.getEventById(eventId);
              Item item = findItem(upc, itemName);
              if (item == null) {
                  item = new Item(upc, itemName);
                  itemRepository.save(item);
              }
              event.addItemNeededForEvent(item, quantity);
              eventRepository.save(event);
              return true;
          }
      As you can see I have to call save on event, otherwise the itemNeeded is not saved. And notice that I also have code that might create a new Item node, and if it does, I have to call itemRepository.save(item) in order for it to be saved and used in itemNeeded.

      The event.addItemNeededForEvent, the Event node is retrieved from a repository, so advanced should proxy that and when I call addItemNeededForEvent that method in the node creates another node

      Code:
      public void addItemNeededForEvent(Item item, BigDecimal quantityNeeded) {
              ItemNeededForEvent itemNeeded = new ItemNeededForEvent(item, this, quantityNeeded);
              if (itemsNeededForEvent == null) {
                  this.itemsNeededForEvent = new HashSet<ItemNeededForEvent>();
              }
              itemsNeededForEvent.add(itemNeeded);
          }
      Instead I would expect my code in advanced mode to only have to look like this

      Code:
      @Override
          public boolean createItemNeededForEvent(Long eventId,
                                               String itemName,
                                               String upc,
                                               BigDecimal quantity) {
              Event event = eventRepository.getEventById(eventId);
              Item item = findItem(upc, itemName);
              if (item == null) {
                  item = new Item(upc, itemName);        }
              event.addItemNeededForEvent(item, quantity);
              return true;
          }
      But having code that looks like that does not work. Nothing is saved there.

      Just because I am afraid you are going to ask, I have to also post my graph config.

      Code:
      <?xml version="1.0" encoding="UTF-8" standalone="no"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:tx="http://www.springframework.org/schema/tx"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/tx
             http://www.springframework.org/schema/tx/spring-tx.xsd
             http://www.springframework.org/schema/data/neo4j
             http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd">
      
      
          <!--neo4j:config storeDirectory="${neo4j.location}"-->
      
          <bean id="graphDatabaseService" class="org.neo4j.kernel.EmbeddedGraphDatabase"
                  destroy-method="shutdown">
              <constructor-arg index="0" value="${neo4j.location}"/>
              <constructor-arg index="1">
                  <map>
                      <entry key="allow_store_upgrade" value="${neo4j.upgrade}"/>
                  </map>
              </constructor-arg>
          </bean>
      
          <!--bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase"
                  destroy-method="shutdown">
              <constructor-arg index="0" value="${neo4j.location}"/>
          </bean-->
          <neo4j:config graphDatabaseService="graphDatabaseService"/>
      
          <neo4j:repositories base-package="com.perfectworldprogramming.eventgate"/>
      
          <bean id="neo4jTransactionManager"
          	   class="org.springframework.transaction.jta.JtaTransactionManager">
              <property name="transactionManager">
                  <bean class="org.neo4j.kernel.impl.transaction.SpringTransactionManager">
                      <constructor-arg ref="graphDatabaseService"/>
                  </bean>
              </property>
              <property name="userTransaction">
                  <bean class="org.neo4j.kernel.impl.transaction.UserTransactionImpl">
                      <constructor-arg ref="graphDatabaseService"/>
                  </bean>
              </property>
              <property name="allowCustomIsolationLevels" value="true"/>
          </bean>
          <tx:annotation-driven mode="proxy"/>
      </beans>
      Mark

      Comment


      • #4
        Hey Mark,

        thanks a lot for the detailed report.

        template.save(item) is needed so that it is attached initially.

        But the event should still be still attached as it comes back from the repository inside of the transaction.

        - Can you check that there is a tx running with template.transactionIsRunning() ?
        - Can you check that event is initially no attached with checking that event.getPersistentState() is not NULL ?

        Would it be possible to extract it into a demo project or unit-test to reproduce? Or share the project with me privately?

        Comment


        • #5
          Originally posted by bytor99999 View Post
          Code:
          @Override
              public boolean createItemNeededForEvent(Long eventId,
                                                   String itemName,
                                                   String upc,
                                                   BigDecimal quantity) {
                  Event event = eventRepository.getEventById(eventId);
                  Item item = findItem(upc, itemName);
                  if (item == null) {
                      item = new Item(upc, itemName);
                      itemRepository.save(item);
                  }
                  event.addItemNeededForEvent(item, quantity);
                  eventRepository.save(event);
                  return true;
              }
          Mark,

          This code looks good, it looks like textbook simple mapping stuff. I think that should work every time.

          But I do think you are using simple mapping. For advanced mapping, the code will look different; see:

          http://static.springsource.org/sprin...ce/html/#setup
          https://github.com/SpringSource/spri...neasts-aspects

          Regards,

          Lasse

          Comment


          • #6
            Yeah, my pom is looking like it is using simple. I have the dependency for advanced, but it is commented out.

            I don't remember when I did that, but I think I did it a while ago because something else didn't work, but that might not be the case anymore.

            Thanks

            Mark

            Comment

            Working...
            X