Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] Cache issue with EclipseLink?

The problem is definitely with your equals and hashCode methods, I would
recommend they only compare the primary key.  If you truly are not changing
the values used in the hashCode, then my guess as to what is occurring is a
cyclic merge loop.  While merging your objects into the shared cache they
are copied, normally through creating a new instance and merging from the
clone into the new instance.  If the merge encounters a relationship, then
it must copy and merge into that object, if that objects also has a
reference to the original object then it must use the current copy of that
object, which may be partially merged.  Merge always merged basic mappings
first, then relationships, but because you are using relationships in your
hashCode and have bi-directional relationships this is occurring.

If you must have your equals method that compare relationships, then make
the hashCode just hash the bascis, it will still be a valid hashCode for the
equals.

You could also try the CloneCopyPolicy, this create a clone of the object on
merge instead of a new instance.  In fact if you are using EclipseLink 1.1
and using field access and weaving a clone copy policy will be used by
default.



Kevin Lester wrote:
> 
> James,
> 
> Thank you for your reply.  The Employee and Manager objects are just
> simplified objects based on more complex entities that are used in our
> application, so I just created the hashcode and equals methods to be
> similar to our apps entity object's hashCode.  In both the test project
> and in the actual application, the fields being used for the hashCode
> don't change, so they are safe to use.  I know it doesn't appear that way
> in my test project (since I have a setter for them), but I do not modify
> those fields so everything is still kosher with the hashCode contract.  I
> noticed that I do modify the Employees rank in the updateEmployeeTest
> which was an oversight, but this test always fails in the setup method due
> to the current issue I am seeing, so the test never even got far enough to
> modify the hashCode.  I removed 'rank' from the Employee's hashCode, and I
> still see the same issue.
> 
> Even though we aren't violating the hashcode or equals contract, it does
> seem that these methods might be part of the problem.  I had to
> specifically add these methods into the Employee object in order to
> reproduce the problem; when I remove these methods everything works fine.  
> But since we aren't violating any contracts, I am at a loss as to why they
> would cause a problem.  Might EclipseLink be internally cloning and
> accessing the object before it is fully cloned, (or possible creating a
> thin version of the object for caching that is not fully filled out), thus
> inadvertently modifying the hashcode?
> 
> Thanks
> Kevin
> 
> 
> James Sutherland wrote:
>> 
>> The problem seems to be your equals and hashCode methods.  Generally
>> these should be left as the default (identity) or only check the primary
>> key.  In particular to Employee hashCode is wrong, a hashCode must be a
>> consistent value, but you are using name and dept which can change, an
>> object's hashCode should never change.  Because you are storing your
>> employees in a Set and changing your hashCode, a remove or contain will
>> no longer work as the objects hashCode has changed.
>> 
>> EclipseLink generally has no dependencies on equals and hashCode unlike
>> other JPA providers, but we still require to be able to remove objects
>> from collections, and that requires that the hashCode and equals are at
>> least correctly implemented.
>> 
>> 
>> 
>> Kevin Lester wrote:
>>> 
>>> All,
>>> 
>>> Recently we have noticed an issue with EclipseLink that appears to be
>>> related to the EclipseLink cache.  Essentially we have noticed that
>>> objects modified via the "merge" method are not always updated in the
>>> cache.  If we merge an object, and then immediately query for the same
>>> object in a different EntityManager, we sometimes get a stale copy of
>>> the object.
>>> Consider the simple test case below:
>>>  - Object A has 2 children (marked @PrivateOwned) and is persisted.  We
>>> see 2 child records in the DB.
>>>  - We then remove a child from Object A, so it now has 1 child.
>>>  - We merge Object A.  The database now has 1 child record in the DB
>>> (which is correct) and the object returned from the merge has 1 child
>>> (which is correct).
>>>  - We close the current EntityManager, get a new one, and then query for
>>> the object we just persisted.  The object returned has 2 children (which
>>> is incorrect).  The extra child record returned is the child record that
>>> we just deleted.  No one else has modified this record, so we would
>>> expect it to only have 1 child.
>>> 
>>> Based on this test case, the cache appears to be out of sync with the
>>> DB, and is returning stale data.  If we clear the cache after all merges
>>> (or disable the cache), everything works properly.  This is also not
>>> 100% reproducible.  We often see the first change or two work properly,
>>> but subsequent changes are not reflected in the cache.  
>>> 
>>> So the question is: what is going on?  This appears to be an EclipseLink
>>> bug to me, but it is definitely possible that we are not using it
>>> properly.
>>> 
>>> To help debug, I have created a "simple" test project that reproduces
>>> the issue the same way we see it in our application.  The test case uses
>>> EclipseLink 1.1-RC2, a mysql DB, and uses an alternative connection pool
>>> than the one provided by EclipseLink (due to some issues we saw).  My
>>> sample project has 4 test cases.  The last test case always fails, and
>>> sometimes the second test fails as well.  Sorry that my simple test case
>>> is so long, but I could not reproduce this issue with a smaller test
>>> case.
>>> 
>>> The relevant code for my test case is below.  I can post the entire
>>> project if required.  I thought I would post this issue here first
>>> before opening a Jira issue in case we are simply doing something wrong.
>>> 
>>> A few Notes:
>>> If I disable the cache in the persistence.xml file, then all test cases
>>> pass.  Similarly, if I remove the following line in the Employee.java
>>> file's hashCode method while leaving the cache enabled, the test cases
>>> pass:
>>>     ret += (manager != null ? manager.hashCode() : 0);
>>> Also, the "setupTest" method always creates new children for the Manager
>>> object, and just copies in their corresponding PK from the current child
>>> objects in the DB.  This is done to simulate how our objects are handled
>>> in the application.
>>> 
>>> Does anyone see anything obvious that we are doing wrong?
>>> Thanks in advance
>>> 
>>> Persistence.xml
>>> <?xml version="1.0" encoding="UTF-8"?>
>>> <persistence xmlns="http://java.sun.com/xml/ns/persistence";
>>>     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
>>>     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
>>>     persistence_1_0.xsd" version="1.0">
>>>     
>>>     <persistence-unit name="testPersistenceUnit" >
>>>     <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
>>>       
>>>       <jta-data-source>java:/comp/env/jdbc/testDs</jta-data-source>
>>>      
>>> <non-jta-data-source>java:/comp/env/jdbc/testDs</non-jta-data-source>
>>>       
>>>       <class>Employee</class>
>>>       <class>Manager</class
>>> 	  <!-- Provider-specific settings -->
>>> 	  <properties>
>>> 		  <property name="eclipselink.ddl-generation"
>>> value="drop-and-create-tables"/> 
>>> 		  <property name="eclipselink.logging.logger"
>>> value="Log4JEclipseLinkLogger"/>
>>> 		  <property name="eclipselink.logging.level" value="FINEST" />
>>> 		  <property name="eclipselink.logging.thread" value="true"/>
>>> 		  <property name="eclipselink.logging.session" value="true"/>
>>> 		  <property name="eclipselink.logging.timestamp" value="true"/>
>>> 		  <property name="eclipselink.logging.exceptions" value="true"/>
>>> 		  
>>> 		  <property name="eclipselink.create-ddl-jdbc-file-name"
>>> value="create-tables.sql"/>
>>> 		  <property name="eclipselink.drop-ddl-jdbc-file-name"
>>> value="drop-tables.sql"/>
>>> 		  <property name="eclipselink.target-database" value="MySQL4"/>
>>> 		  <property name="eclipselink.cache.type.default" value="SoftWeak" /> 
>>> 		  <property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
>>> 		  <property name="eclipselink.jdbc.cache-statements" value="true"/>
>>> 		  <property name="eclipselink.jdbc.cache-statements.size"
>>> value="100"/>
>>> 		  
>>> 		  <!-- The test cases will pass if you uncomment the properties below.
>>> 		  <property name="eclipselink.cache.shared.default" value="false"/>
>>> 		  <property name="eclipselink.cache.size.default" value="0"/>
>>> 		  <property name="eclipselink.cache.type.default" value="None"/>
>>> 		  -->
>>> 	  </properties>  		
>>> 	</persistence-unit>
>>> </persistence>
>>> --------------------------------------------------------
>>> EclipseLinkCacheTest.java:
>>> --------------------------------------------------------
>>> import static org.junit.Assert.assertEquals;
>>> import static org.junit.Assert.fail;
>>> import java.util.Iterator;
>>> import java.util.List;
>>> import javax.naming.Context;
>>> import javax.naming.InitialContext;
>>> import javax.naming.NamingException;
>>> import javax.persistence.EntityManager;
>>> import javax.persistence.EntityManagerFactory;
>>> import javax.persistence.Persistence;
>>> import javax.persistence.Query;
>>> import org.apache.log4j.Logger;
>>> import org.junit.Before;
>>> import org.junit.BeforeClass;
>>> import org.junit.Test;
>>> import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
>>> import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
>>> 
>>> public class EclipseLinkCacheTester {
>>>   protected static EntityManagerFactory emf;
>>>   private static Logger logger =
>>> Logger.getLogger(EclipseLinkCacheTester.class);
>>>   private static final String PERSISTENCE_UNIT = "testPersistenceUnit";
>>>   
>>>   @BeforeClass
>>>   public static void initialize(){
>>>     System.setProperty("LOG_DIR", System.getProperty("user.dir"));
>>>     
>>>     //set up the DB datasource 
>>>     setupInitialContext();
>>>     
>>>     //Set up the entity manager factory
>>>     logger.info("persistanceUnitName: " + PERSISTENCE_UNIT);
>>>     emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
>>>   }
>>>   
>>>   @Before
>>>   public void setupTest(){
>>>     Manager manager = null;
>>>     try{
>>>       manager = this.findManagerByName("Homer", "Sector 7-G");
>>>     } catch (Exception e){
>>>       e.printStackTrace();
>>>       fail("An error occurred getting the manager");
>>>     }
>>>     
>>>     if (manager == null){
>>>       manager = new Manager();
>>>       manager.setName("Homer");
>>>       manager.setDept("Sector 7-G");
>>>     }
>>>     long now = System.currentTimeMillis();
>>>     
>>>     System.out.println("SETUP: Manager'employee count= " +
>>> manager.getEmployees().size());
>>>     Integer lennysID = null, carlsID = null;
>>>     for (Iterator<Employee> iter = manager.getEmployees().iterator();
>>> iter.hasNext();){
>>>       Employee emp = iter.next();
>>>       if (emp.getName().equals("Carl")) carlsID = emp.getEmpId();
>>>       if (emp.getName().equals("Lenny")) lennysID = emp.getEmpId();
>>>       //Remove the child node.
>>>       iter.remove();
>>>     }
>>>     
>>>     Employee lenny = createEmployee(manager, "Lenny", now);
>>>     lenny.setEmpId(lennysID);
>>>     Employee carl = createEmployee(manager, "Carl", now);
>>>     carl.setEmpId(carlsID);
>>>     manager.getEmployees().add(lenny);
>>>     manager.getEmployees().add(carl);
>>>     //Update the manager
>>>     manager = updateManager(manager);
>>>     assertEquals("The number of employees was different than
>>> expected",2,manager.getEmployees().size());
>>> 
>>>     System.out.println("END SETUP: Manager'employee count= " +
>>> manager.getEmployees().size());
>>>   }
>>>   
>>>   @Test
>>>   public void testDoNothing(){
>>>     Manager manager = null;
>>>     try{
>>>       manager = this.findManagerByName("Homer", "Sector 7-G");
>>>     } catch (Exception e){
>>>       fail("Error occurred finding the manager");
>>>     }
>>>     
>>>     assertEquals("The number of employees was different than
>>> expected",2,manager.getEmployees().size());
>>>   }
>>>   
>>>   
>>>   @Test
>>>   public void deleteEmployeeTest(){
>>>     Manager manager = null;
>>>     try{
>>>       manager = this.findManagerByName("Homer", "Sector 7-G");
>>>       Employee lenny = getLenny(manager);
>>>       manager.getEmployees().remove(lenny);
>>>       manager = updateManager(manager);
>>>       assertEquals("The number of employees was different than
>>> expected",1,manager.getEmployees().size());
>>>     } catch (Exception e){
>>>       fail("Error occurred finding the manager");
>>>     }
>>>     
>>>     try{
>>>       manager = this.findManagerByName("Homer", "Sector 7-G");
>>>     } catch (Exception e){
>>>       fail("Error occurred finding the manager");
>>>     }
>>>     assertEquals("The number of employees was different than
>>> expected",1,manager.getEmployees().size());
>>>   }
>>>   
>>>   
>>>   @Test
>>>   public void testAddEmployee(){
>>>     Manager manager = null;
>>>     try{
>>>       manager = this.findManagerByName("Homer", "Sector 7-G");
>>>       manager.getEmployees().add(this.createEmployee(manager, "My.
>>> Burns", System.currentTimeMillis()));
>>>       manager = updateManager(manager);
>>>       assertEquals("The number of employees was different than
>>> expected",3,manager.getEmployees().size());
>>>     } catch (Exception e){
>>>       fail("Error occurred finding the manager");
>>>     }
>>>     
>>>     try{
>>>       manager = this.findManagerByName("Homer", "Sector 7-G");
>>>     } catch (Exception e){
>>>       fail("Error occurred finding the manager");
>>>     }
>>>     assertEquals("The number of employees was different than
>>> expected",3,manager.getEmployees().size());
>>>   }
>>>   
>>>   
>>>   @Test
>>>   public void updateEmployeeTest(){
>>>     Manager manager = null;
>>>     try{
>>>       manager = this.findManagerByName("Homer", "Sector 7-G");
>>>       Employee lenny = getLenny(manager);
>>>       lenny.setRank("Eye-candy");
>>>       manager = updateManager(manager);
>>>       assertEquals("The number of employees was different than
>>> expected",2,manager.getEmployees().size());
>>>       assertEquals("Lenny's rank was not properly
>>> updated","Eye-candy",getLenny(manager).getRank());
>>>     } catch (Exception e){
>>>       fail("Error occurred finding the manager");
>>>     }
>>>     
>>>     try{
>>>       manager = this.findManagerByName("Homer", "Sector 7-G");
>>>     } catch (Exception e){
>>>       fail("Error occurred finding the manager");
>>>     }
>>>     assertEquals("The number of employees was different than
>>> expected",2,manager.getEmployees().size());
>>>     assertEquals("Lenny's rank was not properly
>>> updated","Eye-candy",getLenny(manager).getRank());
>>>   }
>>>   
>>>   
>>>   
>>>   private Employee getLenny(Manager manager){
>>>     for (Iterator<Employee> iter = manager.getEmployees().iterator();
>>> iter.hasNext();){
>>>       Employee emp = iter.next();
>>>       if (emp.getName().equals("Lenny")){
>>>         return emp;
>>>       }
>>>     }
>>>     return null;
>>>   }
>>>   
>>>   private Employee createEmployee(Manager manager, String name, long
>>> now){
>>>     Employee emp = new Employee();
>>>     emp.setManager(manager);
>>>     emp.setName(name);
>>>     emp.setRank("peon");
>>>     emp.setLastModified(now);
>>>     return emp;
>>>   }
>>>     
>>>   
>>>   /**
>>>    * Returns the Manager record that exists for the given Name and Dept
>>> if one exists 
>>>    * @param name the name of the <code>Manager</code>
>>>    * @param dept the department of the <code>Manager</code> 
>>>    * @return the Manager record if one exists
>>>    * @throws Exception if multiple Manager records exist.
>>>    */
>>>   @SuppressWarnings("unchecked")
>>>   private Manager findManagerByName(String name, String dept) throws
>>> Exception{
>>>     EntityManager em = getNewEntityManager();
>>>     try {
>>>       Query q = em.createNamedQuery("GetManagerByNameDept");
>>>       q.setParameter("name", name);
>>>       q.setParameter("dept", dept);
>>>       List<Manager> results = q.getResultList();
>>>       if (results == null || results.size() == 0){
>>>         return null;
>>>       }
>>>       if (results.size() != 1){
>>>         //We have duplicate Build records in the DB, this is illegal
>>>         String errorMsg = "Duplicate Manager records were found with
>>> name = " + name + " and department " + dept; 
>>>         logger.error(errorMsg);
>>>         throw new Exception(errorMsg);
>>>       }
>>>       return (Manager) results.get(0);
>>>     } finally {
>>>       em.close();
>>>     }
>>>   }
>>> 
>>>   
>>>   private Manager updateManager(Manager manager){
>>>     EntityManager em = getNewEntityManager();
>>>     try {
>>>       em.getTransaction().begin();
>>>       manager = em.merge(manager);
>>>       em.getTransaction().commit();
>>>       //Build newBuild=em.find(Build.class, inBuild.getBuildId());
>>>       //System.out.println("#Build files from Build in same session = "
>>> + newBuild.getFiles().size());
>>>     } finally {
>>>       em.close();
>>>     }
>>>     //The manger will be the original object passed in but with the ids
>>> set properly (which could potentially modify the hashCode, depending on
>>> how it is implemented)
>>>     //Since child objects were added prior to the ID being set, there is
>>> a chance that the child objects will not be 
>>>     //retrievable is they were stored in any Collections (because their
>>> hashcode affects their "bucket" in the collection).
>>>     //So we need to return a new object back from scratch which has
>>> everything set correctly.
>>>      return this.getManager(manager.getManagerId());
>>>   }
>>>  
>>>   
>>>   /**
>>>    * This method will get the Manager from the database.
>>>    * 
>>>    * @param managerId
>>>    */
>>>   private Manager getManager(Integer managerId){
>>>     EntityManager em = getNewEntityManager();
>>>     try {
>>>       return em.find(Manager.class, managerId);
>>>     } finally {
>>>       em.close();
>>>     }
>>>   }
>>>  
>>>   
>>>   /**
>>>    * @return
>>>    */
>>>   public EntityManager getNewEntityManager() {
>>>     EntityManager ret=emf.createEntityManager();
>>>     /**
>>>      * bug:
>>> http://forums.oracle.com/forums/thread.jspa?messageID=2284069
>>>      * https://glassfish.dev.java.net/issues/show_bug.cgi?id=3937
>>>      */
>>>     return ret;
>>>   }
>>>   
>>> 
>>>   private static void setupInitialContext(){
>>>     try {
>>>       // Create initial context
>>>       System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
>>> "org.apache.naming.java.javaURLContextFactory");
>>>       System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
>>>       InitialContext ic = new InitialContext();
>>> 
>>>       ic.createSubcontext("java:");
>>>       ic.createSubcontext("java:/comp");
>>>       ic.createSubcontext("java:/comp/env");
>>>       ic.createSubcontext("java:/comp/env/jdbc");      
>>>       
>>>       // Construct DataSource
>>>       MysqlDataSource dataSource = new MysqlConnectionPoolDataSource();
>>>       String dbURL="jdbc:mysql://HOST/klester_eclipselink_test";
>>>       dataSource.setURL(dbURL);
>>>       System.out.println("Running test against db "+dbURL);
>>>       dataSource.setUser(USER);
>>>       dataSource.setPassword(PASSWORD);
>>>       ic.bind("java:/comp/env/jdbc/testDs", dataSource);
>>>     } catch (NamingException ex) {
>>>       ex.printStackTrace();
>>>       logger.error("Error occurred setting the datasource", ex);
>>>     }
>>>   }
>>>   
>>> }
>>> --------------------------------------------------------
>>> Manager.java
>>> --------------------------------------------------------
>>> import java.util.HashSet;
>>> import java.util.Set;
>>> 
>>> import javax.persistence.CascadeType;
>>> import javax.persistence.Column;
>>> import javax.persistence.Entity;
>>> import javax.persistence.FetchType;
>>> import javax.persistence.GeneratedValue;
>>> import javax.persistence.Id;
>>> import javax.persistence.NamedQuery;
>>> import javax.persistence.OneToMany;
>>> import javax.persistence.Table;
>>> 
>>> import org.eclipse.persistence.annotations.PrivateOwned;
>>> 
>>> @Entity
>>> @Table(name = "Manager")
>>> @NamedQuery(name = "GetManagerByNameDept", query = "SELECT obj FROM
>>> Manager obj WHERE obj.name = :name and obj.dept = :dept")
>>> public class Manager {
>>>   private static final int HASH_PRIME = 37;
>>>   
>>>   @Id
>>>   @Column(name = "managerId")
>>>   @GeneratedValue
>>>   private Integer managerId;
>>>   
>>>   private String name = null;
>>>   
>>>   private String dept = null;
>>>   
>>>   @OneToMany (cascade=CascadeType.ALL, mappedBy="manager",
>>> fetch=FetchType.EAGER)
>>>   @PrivateOwned
>>>   private Set<Employee> employees = new HashSet<Employee>();
>>> 
>>>   public Integer getManagerId() {
>>>     return managerId;
>>>   }
>>> 
>>>   public void setManagerId(Integer managerId) {
>>>     this.managerId = managerId;
>>>   }
>>> 
>>>   public String getName() {
>>>     return name;
>>>   }
>>> 
>>>   public void setName(String name) {
>>>     this.name = name;
>>>   }
>>> 
>>>   public String getDept() {
>>>     return dept;
>>>   }
>>> 
>>>   public void setDept(String dept) {
>>>     this.dept = dept;
>>>   }
>>> 
>>>   public Set<Employee> getEmployees() {
>>>     return employees;
>>>   }
>>> 
>>>   public void setEmployees(Set<Employee> employees) {
>>>     this.employees = employees;
>>>   }
>>>   
>>>   @Override
>>>   public int hashCode() {
>>>     int ret = 0;
>>>     ret += HASH_PRIME * ((dept != null ? dept.hashCode() : 0));
>>>     ret += HASH_PRIME * ((name != null ? name.hashCode() : 0));
>>>     return ret;
>>>   } 
>>>   
>>> //  @Override
>>>   public boolean equals(Object obj) {
>>>     Manager other = (Manager) obj;
>>> 
>>>     return  ((dept != null ? dept.equals(other.dept) : (other.dept !=
>>> null ? false : true)))
>>>         && ((name != null ? name.equals(other.name) : (other.name !=
>>> null ? false : true)));
>>>   }
>>>   
>>> }
>>> --------------------------------------------------------
>>> Employee.java
>>> --------------------------------------------------------
>>> 
>>> import javax.persistence.CascadeType;
>>> import javax.persistence.Column;
>>> import javax.persistence.Entity;
>>> import javax.persistence.GeneratedValue;
>>> import javax.persistence.GenerationType;
>>> import javax.persistence.Id;
>>> import javax.persistence.JoinColumn;
>>> import javax.persistence.ManyToOne;
>>> import javax.persistence.Table;
>>> import javax.persistence.TableGenerator;
>>> 
>>> 
>>> @Entity
>>> @Table(name = "Employee")
>>> public class Employee {
>>>  
>>>   @Id
>>>   @Column(name = "empId")
>>>   @TableGenerator(
>>>         name="EMP_ID_SEQ",
>>>         table="SEQUENCE",
>>>         allocationSize=100
>>>     )
>>>   @GeneratedValue(strategy = GenerationType.AUTO, generator =
>>> "EMP_ID_SEQ")
>>>   private Integer empId;
>>>   
>>>   private String name = null;
>>>   
>>>   private String rank = null;
>>>   
>>>   @ManyToOne (cascade=CascadeType.REFRESH)
>>>   @JoinColumn(name="managerId")
>>>   private Manager manager = null;
>>> 
>>>   private long lastModified = 0L;
>>>   
>>>   public String getName() {
>>>     return name;
>>>   }
>>> 
>>>   public void setName(String name) {
>>>     this.name = name;
>>>   }
>>> 
>>>   public String getRank() {
>>>     return rank;
>>>   }
>>> 
>>>   public void setRank(String rank) {
>>>     this.rank = rank;
>>>   }
>>> 
>>>   public Manager getManager() {
>>>     return manager;
>>>   }
>>> 
>>>   public void setManager(Manager manager) {
>>>     this.manager = manager;
>>>   }
>>> 
>>>   public Integer getEmpId() {
>>>     return empId;
>>>   }
>>> 
>>>   public void setEmpId(Integer empId) {
>>>     this.empId = empId;
>>>   }
>>> 
>>>   public long getLastModified() {
>>>     return lastModified;
>>>   }
>>> 
>>>   public void setLastModified(long lastModified) {
>>>     this.lastModified = lastModified;
>>>   }
>>> 
>>>   @Override
>>>   public int hashCode() {
>>>     int ret = 0;
>>>     ret += (name != null ? name.hashCode() : 0);
>>>     ret += (rank != null ? rank.hashCode() : 0);
>>>     //Removing the line of code below makes the tests pass.
>>>     ret += (manager != null ? manager.hashCode() : 0);
>>>     return ret;
>>>   }
>>> 
>>>   
>>>   @Override
>>>   public boolean equals(Object obj) {
>>>     Employee other = (Employee) obj;
>>>     
>>>     //return hashCode() == other.hashCode();
>>>     return  (name != null ? name.equals(other.name) : other.name != null
>>> ? false : true)
>>>       && (rank != null ? rank.equals(other.rank) : other.rank != null ?
>>> false : true)
>>>       && (manager != null ? manager.equals(other.manager) :
>>> other.manager != null ? false : true);
>>>   }
>>>   
>>> }
>>> 
>>> 
>> 
>> 
> 
> 


-----
---
http://wiki.eclipse.org/User:James.sutherland.oracle.com James Sutherland 
http://www.eclipse.org/eclipselink/
 EclipseLink ,  http://www.oracle.com/technology/products/ias/toplink/
TopLink 
Wiki:  http://wiki.eclipse.org/EclipseLink EclipseLink , 
http://wiki.oracle.com/page/TopLink TopLink 
Forums:  http://forums.oracle.com/forums/forum.jspa?forumID=48 TopLink , 
http://www.nabble.com/EclipseLink-f26430.html EclipseLink 
Book:  http://en.wikibooks.org/wiki/Java_Persistence Java Persistence 
-- 
View this message in context: http://www.nabble.com/Cache-issue-with-EclipseLink--tp22440339p22475611.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.



Back to the top