2011-04-25 36 views
0

我們有一個簡單的父子關係,爲了防止調用代碼修改關係,我們在get方法上使用google集合-ImmutableSet.copyOf。Hibernate在收集未修改時發佈更新

public Set<OrganizationalUnit> getChildren() 
    { 
     return ImmutableSet.copyOf(children); 
    } 

儘管流中沒有任何修改,但hibernate正在發佈更新語句。任何人都可以解釋在幕後發生了什麼,以及爲什麼當我們剛剛創建從DB檢索到的集合的副本時,hibernate會將集合看作是髒的。

以下是在HBM文件,映射類和相應的測試情形

OrganizationalUnitCatalog.java

package com.test.domain.product; 

import java.util.Collection; 
import java.util.List; 
import java.util.Set; 

import org.springframework.util.CollectionUtils; 

import com.google.common.base.Function; 
import com.google.common.collect.ImmutableMap; 
import com.google.common.collect.ImmutableSet; 
import com.google.common.collect.Lists; 
import com.google.common.collect.Maps; 

public class OrganizationalUnitCatalog 
{ 
    private Long id; 

    private Long systemId; 

    private String code; 

    private Set<OrganizationalUnit> children; 




    public Set<OrganizationalUnit> getChildren() 
    { 
     return ImmutableSet.copyOf(children); 
    } 

    public void setChildren(final Set<OrganizationalUnit> products) 
    { 
     this.children = products; 
    } 

    public Long getSystemId() 
    { 
     return systemId; 
    } 

    public void setSystemId(final Long systemId) 
    { 
     this.systemId = systemId; 
    } 

    public Long getId() 
    { 
     return id; 
    } 

    public void setId(final Long id) 
    { 
     this.id = id; 
    } 

    @Override 
    public String toString() 
    { 
     return String.format("OrganizationalUnitCatalog [id=%s, systemId=%s, products=%s]", id, systemId, children.size()); 
    } 

    public void setCode(final String code) 
    { 
     this.code = code; 
    } 

    public String getCode() 
    { 
     return code; 
    } 

    @Override 
    public int hashCode() 
    { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + ((code == null) ? 0 : code.hashCode()); 
     result = prime * result + ((systemId == null) ? 0 : systemId.hashCode()); 
     return result; 
    } 

    @Override 
    public boolean equals(final Object obj) 
    { 
     if (this == obj) 
     { 
      return true; 
     } 
     if (obj == null) 
     { 
      return false; 
     } 
     if (getClass() != obj.getClass()) 
     { 
      return false; 
     } 
     OrganizationalUnitCatalog other = (OrganizationalUnitCatalog) obj; 
     if (code == null) 
     { 
      if (other.code != null) 
      { 
       return false; 
      } 
     } 
     else if (!code.equals(other.code)) 
     { 
      return false; 
     } 
     if (systemId == null) 
     { 
      if (other.systemId != null) 
      { 
       return false; 
      } 
     } 
     else if (!systemId.equals(other.systemId)) 
     { 
      return false; 
     } 
     return true; 
    } 

} 

OrganizationalUnitCatalog.hbm文件

<hibernate-mapping package="com.test.domain.product"> 
    <class name="OrganizationalUnitCatalog" table="ORGANIZATIONALUNITCATALOG"> 
    <cache usage="read-write" /> 
     <id name="id" type="java.lang.Long" column="ID"> 
      <generator class="native"> 
       <param name="sequence">ORGANIZATIONALUNITCATALOG_SN</param> 
      </generator> 
     </id> 
     <property name="systemId" type="java.lang.Long" column="SYSTEMID" not-null="true" /> 
     <property name="code" type="java.lang.String" column="CODE" not-null="true" /> 
     <set lazy="false" name="children" table="ORGANIZATIONALUNIT" cascade="all" fetch="join" > 
      <cache usage="read-write" /> 
      <key> 
       <column name="ORGANIZATIONALUNITCATALOG_ID" /> 
      </key> 
      <one-to-many class="OrganizationalUnit" /> 
     </set> 
    </class> 
    <query name="HibernateOrganizationalUnitCatalogDao.findBySystemId">from OrganizationalUnitCatalog where systemId = :systemId</query> 
</hibernate-mapping> 

OrganizationalUnit.java

package com.test.domain.product; 

import java.util.Collections; 
import java.util.Date; 
import java.util.Set; 
import java.util.Stack; 

import com.google.common.base.Joiner; 
import com.google.common.collect.ImmutableSet; 
import com.google.common.collect.Sets; 


public class OrganizationalUnit 
{ 
    public static final String QUALIFIED_NAME_SEPARATOR = "/"; 

    private static final Joiner JOINER = Joiner.on(QUALIFIED_NAME_SEPARATOR); 

    private Long id; 

    private OrganizationalUnit parent; 

    private Set<OrganizationalUnit> children = Collections.emptySet(); 


    private String code; 

    private String name; 

    private Date startDate; 

    private Date endDate; 

    private boolean decisionable; 

    private boolean selectable; 

    private String qualifiedCode; 


    public void addChild(final OrganizationalUnit child) 
    { 
     /* 
     * Preconditions.checkState(systemId != null, 
     * "Set the systemId before adding children. This ensures all children have the systemId when persisted."); 
     */ 
     if (children.isEmpty()) 
     { 
      children = Sets.newHashSet(); 
     } 
     child.setParent(this); 
     children.add(child); 
    } 


    public String getQualifiedCode() 
    { 
     if (qualifiedCode != null) 
     { 
      // use the cache 
      return qualifiedCode; 
     } 
     if (parent == null) 
     { 
      qualifiedCode = code; 
      return qualifiedCode; 
     } 

     Stack<String> s = new Stack<String>(); 
     OrganizationalUnit p = parent; 
     while (p != null) 
     { 
      s.push(p.getCode()); 
      p = p.getParent(); 
     } 
     qualifiedCode = JOINER.join(s) + QUALIFIED_NAME_SEPARATOR + code; 

     return qualifiedCode; 
    } 

    public Long getId() 
    { 
     return id; 
    } 

    public void setId(final Long id) 
    { 
     this.id = id; 
    } 

    public String getCode() 
    { 
     return code; 
    } 

    public void setCode(final String code) 
    { 
     this.code = code; 
    } 

    public String getName() 
    { 
     return name; 
    } 

    public void setName(final String name) 
    { 
     this.name = name; 
    } 

    public Date getStartDate() 
    { 
     return startDate; 
    } 

    public void setStartDate(final Date startDate) 
    { 
     this.startDate = startDate; 
    } 

    public Date getEndDate() 
    { 
     return endDate; 
    } 

    public void setEndDate(final Date endDate) 
    { 
     this.endDate = endDate; 
    } 

    public boolean isDecisionable() 
    { 
     return decisionable; 
    } 

    public void setDecisionable(final boolean isDecisionable) 
    { 
     this.decisionable = isDecisionable; 
    } 

    public boolean isSelectable() 
    { 
     return selectable; 
    } 

    public void setSelectable(final boolean isSelectable) 
    { 
     this.selectable = isSelectable; 
    } 

    public OrganizationalUnit getParent() 
    { 
     return parent; 
    } 

    public void setParent(final OrganizationalUnit parent) 
    { 
     this.parent = parent; 
    } 

    public Set<OrganizationalUnit> getChildren() 
    { 
     return ImmutableSet.copyOf(children); 
    } 

    public void setChildren(final Set<OrganizationalUnit> children) 
    { 
     this.children = children; 
    } 

    public void setQualifiedCode(final String qCode) 
    { 
     this.qualifiedCode = qCode; 
    } 

    public Set<ZipCodePreferenceEntry> getBureauPreferences() 
    { 
     return bureauPreferences; 
    } 

    public void setBureauPreferences(final Set<ZipCodePreferenceEntry> bureauPreferences) 
    { 
     this.bureauPreferences = bureauPreferences; 
    } 

    @Override 
    public String toString() 
    { 
     return String 
       .format("OrganizationalUnit [getCode=%s, getQualifiedCode=%s, getName=%s, getParent=%s, getChildren=%s, getStartDate=%s, getEndDate=%s, isDecisionable=%s, isSelectable=%s, getId=%s]", 
         getCode(), getQualifiedCode(), getName(), getParent(), getChildren().size(), getStartDate(), getEndDate(), 
         isDecisionable(), isSelectable(), getId()); 
    } 

    @Override 
    public int hashCode() 
    { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + ((getQualifiedCode() == null) ? 0 : getQualifiedCode().hashCode()); 
     return result; 
    } 

    @Override 
    public boolean equals(final Object obj) 
    { 
     if (this == obj) 
     { 
      return true; 
     } 
     if (obj == null) 
     { 
      return false; 
     } 
     if (getClass() != obj.getClass()) 
     { 
      return false; 
     } 
     OrganizationalUnit other = (OrganizationalUnit) obj; 
     if (getQualifiedCode() == null) 
     { 
      if (other.getQualifiedCode() != null) 
      { 
       return false; 
      } 
     } 
     else if (!getQualifiedCode().equals(other.getQualifiedCode())) 
     { 
      return false; 
     } 
     return true; 
    } 
} 

OrganizationalUnit.hbm文件

<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
            "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping package="com.test.domain.product"> 
    <class name="OrganizationalUnit" table="ORGANIZATIONALUNIT"> 
     <cache usage="read-write" /> 
     <id name="id" type="java.lang.Long" column="ID"> 
      <generator class="native"> 
       <param name="sequence">ORGANIZATIONALUNIT_SN</param> 
      </generator> 
     </id> 
     <many-to-one name="parent" class="OrganizationalUnit" lazy="false" column="PARENT" /> 
     <set name="children" lazy="false" fetch="join" table="ORGANIZATIONALUNIT" cascade="all"> 
      <cache usage="read-write" /> 
      <key> 
       <column name="PARENT" /> 
      </key> 
      <one-to-many class="OrganizationalUnit" /> 
     </set>  
     <property name="code" type="java.lang.String" column="CODE" not-null="true" /> 
     <property name="name" type="java.lang.String" column="NAME" /> 
     <property name="qualifiedCode" type="java.lang.String" column="QUALIFIEDCODE" /> 
     <property name="startDate" type="java.util.Date" column="STARTDATE" /> 
     <property name="endDate" type="java.util.Date" column="ENDDATE" /> 
     <property name="decisionable" type="boolean" column="ISDECISIONABLE" /> 
     <property name="selectable" type="boolean" column="ISSELECTABLE" /> 
    </class> 
</hibernate-mapping> 

測試用例

package com.equifax.ic.platform.domain.product; 

import java.sql.SQLException; 
import java.util.List; 
import java.util.Set; 

import org.hibernate.HibernateException; 
import org.hibernate.Query; 
import org.hibernate.Session; 
import org.hibernate.SessionFactory; 
import org.hibernate.Transaction; 
import org.hibernate.cfg.Configuration; 
import org.hibernate.cfg.Environment; 
import org.hibernate.dialect.HSQLDialect; 
import org.junit.Before; 
import org.junit.Test; 
import org.springframework.orm.hibernate3.HibernateCallback; 
import org.springframework.orm.hibernate3.HibernateTemplate; 
import org.springframework.orm.hibernate3.HibernateTransactionManager; 
import org.springframework.transaction.PlatformTransactionManager; 
import org.springframework.transaction.support.TransactionTemplate; 

import com.google.common.collect.Lists; 
import com.google.common.collect.Sets; 

public class Test1 
{ 
    private OrganizationalUnitCatalog catalog; 

    private OrganizationalUnit mortgage; 

    protected HibernateTemplate hibernateTemplate; 

    protected TransactionTemplate transTemplate; 

    protected PlatformTransactionManager transactionManager; 

    @Before 
    public void init() 
    { 
     catalog = new OrganizationalUnitCatalog(); 
     catalog.setCode("test"); 
     catalog.setSystemId(Long.valueOf(0)); 

     mortgage = new OrganizationalUnit(); 
     mortgage.setCode("Mortgage"); 
     OrganizationalUnit ml = new OrganizationalUnit(); 
     ml.setCode("Mortgage Loan"); 
     OrganizationalUnit me = new OrganizationalUnit(); 
     me.setCode("Home Equity LOC"); 

     mortgage.addChild(me); 
     mortgage.addChild(ml); 
     // add unit to catalog 
     catalog.setChildren(Sets.newHashSet(mortgage)); 
    } 

    @Test 
    public void updateIssue() 
    { 
     hibernateTemplate.save(catalog); 

     hibernateTemplate.execute(new HibernateCallback() { 
      public Object doInHibernate(Session session) throws HibernateException, SQLException 
      { 
       Transaction tx = session.beginTransaction(); 
       Query query = session 
         .createQuery("from com.test.domain.product.OrganizationalUnitCatalog ouc where ouc.systemId=0"); 
       query.setMaxResults(1); 
       OrganizationalUnitCatalog entity = (OrganizationalUnitCatalog) query.list().get(0); 
       Set<OrganizationalUnit> children = entity.getChildren(); 
       System.out.println("Children count :" + children.size()); 

       tx.commit(); 

       return null; 
      } 
     }); 
    } 

    @Before 
    public void setUp() throws Exception 
    { 
     Configuration configuration = new Configuration(); 
     configuration.setProperty(Environment.DRIVER, getDriverClassName()); 
     configuration.setProperty(Environment.URL, getJdbcUrl()); 
     configuration.setProperty(Environment.USER, getuserName()); 
     configuration.setProperty(Environment.PASS, getPassword()); 
     configuration.setProperty(Environment.DIALECT, getHibernateDialect()); 
     configuration.setProperty(Environment.SHOW_SQL, "true"); 
     configuration.setProperty(Environment.HBM2DDL_AUTO, getHbm2DdlAuto()); 
     configuration.setProperty(Environment.STATEMENT_BATCH_SIZE, "5"); 
     configuration.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "true"); 
     configuration.setProperty(Environment.USE_QUERY_CACHE, "true"); 
     configuration.setProperty(Environment.CACHE_PROVIDER, "org.hibernate.cache.EhCacheProvider"); 

     for (String resource : getHbmResourceUnderTest()) 
     { 
      configuration.addResource(resource); 
     } 
     SessionFactory sessionFactory = configuration.buildSessionFactory(); 
     // OracleLobHandler lobHandler = new OracleLobHandler(); 
     // lobHandler.setNativeJdbcExtractor(new SimpleNativeJdbcExtractor()); 
     // 
     // ((LocalSessionFactoryBean) sessionFactory).setLobHandler(lobHandler); 

     transactionManager = new HibernateTransactionManager(sessionFactory); 
     hibernateTemplate = new HibernateTemplate(sessionFactory); 
     transTemplate = new TransactionTemplate(transactionManager); 
    } 

    protected String getHbm2DdlAuto() 
    { 
     return "create-drop"; 
    } 

    protected String getPassword() 
    { 
     return ""; 
    } 

    protected String getHibernateDialect() 
    { 
     return HSQLDialect.class.getName(); 
    } 

    protected String getuserName() 
    { 
     return "sa"; 
    } 

    protected String getJdbcUrl() 
    { 
     return "jdbc:hsqldb:mem:test"; 
    } 

    protected String getDriverClassName() 
    { 
     return "org.hsqldb.jdbcDriver"; 
    } 

    public List<String> getHbmResourceUnderTest() 
    { 
     return Lists 
       .newArrayList("com/test/domain/OrganizationalUnit.hbm.xml", "com/test/domain/OrganizationalUnitCatalog.hbm.xml"); 
    } 
} 

測試日誌

Hibernate: insert into ORGANIZATIONALUNITCATALOG (ID, SYSTEMID, CODE) values (null, ?, ?) 
Hibernate: call identity() 
Hibernate: insert into ORGANIZATIONALUNIT (ID, PARENT, CODE, NAME, QUALIFIEDCODE, STARTDATE, ENDDATE, ISDECISIONABLE, ISSELECTABLE) values (null, ?, ?, ?, ?, ?, ?, ?, ?) 
Hibernate: call identity() 
Hibernate: insert into ORGANIZATIONALUNIT (ID, PARENT, CODE, NAME, QUALIFIEDCODE, STARTDATE, ENDDATE, ISDECISIONABLE, ISSELECTABLE) values (null, ?, ?, ?, ?, ?, ?, ?, ?) 
Hibernate: call identity() 
Hibernate: insert into ORGANIZATIONALUNIT (ID, PARENT, CODE, NAME, QUALIFIEDCODE, STARTDATE, ENDDATE, ISDECISIONABLE, ISSELECTABLE) values (null, ?, ?, ?, ?, ?, ?, ?, ?) 
Hibernate: call identity() 
Hibernate: update ORGANIZATIONALUNIT set ORGANIZATIONALUNITCATALOG_ID=? where ID=? 
Hibernate: update ORGANIZATIONALUNIT set PARENT=? where ID=? 
Hibernate: update ORGANIZATIONALUNIT set PARENT=? where ID=? 
Hibernate: select top ? organizati0_.ID as ID1_, organizati0_.SYSTEMID as SYSTEMID1_, organizati0_.CODE as CODE1_ from ORGANIZATIONALUNITCATALOG organizati0_ where organizati0_.SYSTEMID=0 
Hibernate: select children0_.ORGANIZATIONALUNITCATALOG_ID as ORGANIZ10_1_, children0_.ID as ID1_, children0_.ID as ID0_0_, children0_.PARENT as PARENT0_0_, children0_.CODE as CODE0_0_, children0_.NAME as NAME0_0_, children0_.QUALIFIEDCODE as QUALIFIE5_0_0_, children0_.STARTDATE as STARTDATE0_0_, children0_.ENDDATE as ENDDATE0_0_, children0_.ISDECISIONABLE as ISDECISI8_0_0_, children0_.ISSELECTABLE as ISSELECT9_0_0_ from ORGANIZATIONALUNIT children0_ where children0_.ORGANIZATIONALUNITCATALOG_ID=? 
Hibernate: select children0_.PARENT as PARENT1_, children0_.ID as ID1_, children0_.ID as ID0_0_, children0_.PARENT as PARENT0_0_, children0_.CODE as CODE0_0_, children0_.NAME as NAME0_0_, children0_.QUALIFIEDCODE as QUALIFIE5_0_0_, children0_.STARTDATE as STARTDATE0_0_, children0_.ENDDATE as ENDDATE0_0_, children0_.ISDECISIONABLE as ISDECISI8_0_0_, children0_.ISSELECTABLE as ISSELECT9_0_0_ from ORGANIZATIONALUNIT children0_ where children0_.PARENT=? 
Hibernate: select children0_.PARENT as PARENT1_, children0_.ID as ID1_, children0_.ID as ID0_0_, children0_.PARENT as PARENT0_0_, children0_.CODE as CODE0_0_, children0_.NAME as NAME0_0_, children0_.QUALIFIEDCODE as QUALIFIE5_0_0_, children0_.STARTDATE as STARTDATE0_0_, children0_.ENDDATE as ENDDATE0_0_, children0_.ISDECISIONABLE as ISDECISI8_0_0_, children0_.ISSELECTABLE as ISSELECT9_0_0_ from ORGANIZATIONALUNIT children0_ where children0_.PARENT=? 

Hibernate: select children0_.PARENT as PARENT1_, children0_.ID as ID1_, children0_.ID as ID0_0_, children0_.PARENT as PARENT0_0_, children0_.CODE as CODE0_0_, children0_.NAME as NAME0_0_, children0_.QUALIFIEDCODE as QUALIFIE5_0_0_, children0_.STARTDATE as STARTDATE0_0_, children0_.ENDDATE as ENDDATE0_0_, children0_.ISDECISIONABLE as ISDECISI8_0_0_, children0_.ISSELECTABLE as ISSELECT9_0_0_ from ORGANIZATIONALUNIT children0_ where children0_.PARENT=? 
Children count :1 
Hibernate: update ORGANIZATIONALUNIT set ORGANIZATIONALUNITCATALOG_ID=null where ORGANIZATIONALUNITCATALOG_ID=? 
Hibernate: update ORGANIZATIONALUNIT set PARENT=null where PARENT=? 
Hibernate: update ORGANIZATIONALUNIT set ORGANIZATIONALUNITCATALOG_ID=? where ID=? 
Hibernate: update ORGANIZATIONALUNIT set PARENT=? where ID=? 
Hibernate: update ORGANIZATIONALUNIT set PARENT=? where ID=? 
Hibernate: update ORGANIZATIONALUNIT set ORGANIZATIONALUNITCATALOG_ID=null where ORGANIZATIONALUNITCATALOG_ID=? 
Hibernate: update ORGANIZATIONALUNIT set PARENT=null where PARENT=? 
Hibernate: update ORGANIZATIONALUNIT set ORGANIZATIONALUNITCATALOG_ID=? where ID=? 
Hibernate: update ORGANIZATIONALUNIT set PARENT=? where ID=? 
Hibernate: update ORGANIZATIONALUNIT set PARENT=? where ID=? 

感謝 Indrani

回答

1

也許你可以配置Hibernate使用字段訪問您的收藏:

<set name="children" access = "field" ...> 
    ... 
</set> 
相關問題