Announcement Announcement Module
Collapse
No announcement yet.
Data not committed without flush or transaction Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Data not committed without flush or transaction

    hi,

    sometimes I think I finally understand hibernate then an issue comes along and suddenly I think I don't understand it at all.

    I have an Object Project which contains a Set (one-to-many) of ProjectOccurences. In the following method I add a new ProjectOccurence to an existing Project, change some data of the project and then update it to the database:

    The service:
    Code:
    	
    public void updateProjectStatus (Integer projectID, String status)
    {
    	Project p = projectDAO.getProject(projectID);
    	ProjectOccurence po = new ProjectOccurence();
                 po.setVersion(p.getVersion() + 1);//was 1 so will be 2
    	//set some data on this...
    	
    	p.setVersion(p.getVersion() + 1);//was 1 so will be 2
    	p.getProjectOccurences().add(po);
    	projectDAO.update(p); //calls getHibernateTemplate().update(project)
    }
    here is the test-code:
    Code:
    ...
    projectService.updateStatus(id, "PENDING ACCEPTANCE");
    p = projectService.getProject(id);
    assertNotNull(p);
    assertEquals(2, p.getVersion().intValue());
    And this goes fine, all assertions succeed. However when I look in the database after this point the version of the project is still 1 and the new ProjectOccurence was not committed to the database! (Also in the log I don't see any insert or update statements)

    I found two ways to solve the problem:
    1. after calling getHibernateTemplate().update(project) call getHibernateTemplate().flush()
    2. declare a transaction around the service-method updateStatus

    both solutions immediately commit the data in the database but I assume solution 1 should not be done (don't really fully understand why) and I don't understand why I need to declare a transaction myself (of course in this case I would put these actions in a transaction but not when I'm for example simply updating a project after changing one of its fields).

    I'm not only asking this to understand it but also because of the following:
    if I do a simple load of the Project (projectService.getProject(id) ) it has the correct version number (< first level cache I assume). But if I execute following query:

    Code:
    //get ProjectOccurence where version = version of given project
    public ProjectOccurence getLastProjectOccurence(Integer projectID)
    {
    	return (ProjectOccurence) getUnique(getHibernateTemplate().findByNamedParam("select po from Project p, ProjectOccurence po where p.ID = :pID and p.version = po.version", "pID", projectID));
    	}
    This returns the ProjectOccurence with version 1 (as it is in the database) and not version 2 (as in the objects).

    Any help/explanation would be much appreciated!

    tx,
    Stijn

  • #2
    Why do you update the version field manually ? AFAIK, Hibernate is managing this automatically, unless you want to save your object on purpose, even if it was not changed.
    Can you post details about configuration ?

    Comment


    • #3
      Originally posted by Andrei Stefan View Post
      Why do you update the version field manually ? AFAIK, Hibernate is managing this automatically, unless you want to save your object on purpose, even if it was not changed.
      Can you post details about configuration ?
      yeah, it's maybe a bit confusing because I didn't post all my code. First, it's not only the version that is changed, some other fields change as well (just didn't add that to the above code). Second, the version-number needs to be set manualy, it only increases if certain fields change (and some other conditions).

      possibly related config:
      Code:
      	<bean id="dataSource"
      		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      		<property name="url" value="jdbc:mysql://localhost:3306/fod" />
      		<property name="driverClassName" value="org.gjt.mm.mysql.Driver" />
      	</bean>
      
      	<bean id="sessionFactory"
      		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
      		<property name="dataSource"><ref local="dataSource"/></property>
      		<property name="hibernateProperties">
      			<props>
      				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
      				<prop key="hibernate.hbm2ddl.auto">update</prop>
      				<prop key="hibernate.show_sql">true</prop>
      				<prop key="hibernate.format_sql">true</prop>
      			</props>
      		</property>
      		<property name="mappingDirectoryLocations">
      			<list>
      				<value>classpath:/com/laco/planningtool</value>
      			</list>
      		</property>
      	</bean>
      
      	<bean name="projectDAO" class="com.laco.planningtool.dao.ProjectDAOImpl"
      		autowire="byName" />
      
      	<bean name="projectServiceTarget"
      		class="com.laco.planningtool.service.ProjectServiceImpl">
      		<property name="projectDAO" ref="projectDAO" />
      	</bean>
      thanks!

      Comment


      • #4
        Are you running this from the Spring test classes? If that's the case your test is contained within a transation by default and won't actually ever commit, hence you not seeing the changes. If you post your test case this might help to confirm it.
        Last edited by karldmoore; Aug 29th, 2007, 11:02 AM.

        Comment


        • #5
          Originally posted by karldmoore View Post
          Are you running this from the Spring test classes? If that's the case your test is contained within a transation by default and won't actually ever commit, hence you not seeing the changes. If you post your test case this might help to confirm it.
          I'm simply extending TestCase:
          Code:
          	
          public class ProjectSysTest extends TestCase
          {
          	private static ApplicationContext context;
          	static SessionFactory sessionFactory;
          	static ProjectService projectService;
          	static UserService userService;
          	Session session;
          
                       protected void setUp() throws Exception
          	{
          		super.setUp();
          		String[] contextLocations = new String[2];
          		contextLocations[0] = System.getProperty("user.dir") + "\\webapp\\WEB-INF\\planningtool-data-unittesting.xml";
          		contextLocations[1] = System.getProperty("user.dir") + "\\webapp\\WEB-INF\\planningtool-service.xml";
          		context = new FileSystemXmlApplicationContext(contextLocations);
          		sessionFactory = (SessionFactory) context.getBean("sessionFactory");
          		session = SessionFactoryUtils.getSession(sessionFactory, true);
          		TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
          
          		projectService = (ProjectService) context.getBean("projectService");
          		userService = (UserService) context.getBean("userService");
          	}
          
          	protected void tearDown() throws Exception
          	{
          		SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
          		SessionFactoryUtils.releaseSession(sessionHolder.getSession(), sessionFactory);
          		context = null;
          		super.tearDown();
          	}
          
          	public void testCreateProject() throws Exception
          	{
                                    //...
                       }
          }

          Comment


          • #6
            I think you have to flush your session manually before closing it.

            Comment


            • #7
              Originally posted by Andrei Stefan View Post
              I think you have to flush your session manually before closing it.
              tx,
              as I said that does the trick but I don't really understand why it is required or why it also work if you do it inside a transaction. Doesn't the hibernateTemplate flush the session? at least before xecuting a select? Does ending a transaction flush the session?

              Comment


              • #8
                TransactionSynchronizationManager is simply a wrapper for a thread local - in your case it binds the session factory to the thread.
                Doesn't the hibernateTemplate flush the session?
                it does flush the session, but only when it runs inside a transaction, or when the flush_mode is set to EAGER and in your case there is no tx management and I suppose you didn't set flush_mode to EAGER, either.

                Does ending a transaction flush the session?
                In case of HibernateTransactionManager, when committing, the flush is called automatically.

                In your case I see no tx management.

                Comment


                • #9
                  Andrei,

                  thanks for the clarification!

                  greetz,
                  Stijn

                  Comment


                  • #10
                    There is something more I'd like to add.
                    If you look at the HibernateTemplate source code, every call to a method that updates/saves/add etc goes through execute() method which calls a method which decides to flush or not:
                    Code:
                    if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() != FLUSH_NEVER)) {
                    			logger.debug("Eagerly flushing Hibernate session");
                    			session.flush();
                    		}
                    'existingTransaction' is defined by the association of the checked session and sessionFactory to the current thread. But, you manually added those session and sessionFactory by binding it using TransactionSynchronizationManager and that's why 'existingTransaction' will have 'true' value and, in this way, the flush will not happen.

                    Comment

                    Working...
                    X