Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] Error pulling back records with FetchType=LAZY

Could you include the full exception for the stack trace.  Also include what
is occurring when the error occurs, you seem to have multiple threads
inserting objects, is that correct, how many threads, do they each use a
different EntityManager?  Also the code for EmpCreator, or ExecutorService,
ExecutorCompletionService.



Kevin Lester wrote:
> 
> We're testing EclipseLink 1.1, and we're getting an error while retrieving
> child records that are marked as lazy.  Specifically we receive the
> following error:
> ECLIPSELINK-02004: A signal was attempted before wait() on
> ConcurrencyManager. This normally means that an attempt was made to
> {0}commit or rollback a transaction before it was started, or to rollback
> a transaction twice.
> 
> The error gets thrown from a line of code like the following:
> parent.getChildren().size();
> The getChildren() method returns a Set of Child objects that have a
> fetchType of LAZY.  This code used to work with EclipseLink 1.0.2.
> 
> To debug the issue, I created a small test project that recreates the
> situation in which we see the error.  The code for the test project is
> below.  An error gets thrown on the expected line of code in my test
> project - but interestingly it is a different error than we get in our
> application.  I was not able to reproduce the exact error in my test
> project for some reason.  Test project code below:
> 
> 
> ----------------------------EclipseLinkLazyTester class
> ----------------------------------------
> public class EclipseLinkLazyTester {
>   protected static EntityManagerFactory emf;
>   private static Logger logger =
> Logger.getLogger(EclipseLinkLazyTester.class);
>   private static final String PERSISTENCE_UNIT = "testPersistenceUnit";
>   private static final int NUM_EMPLOYEES=59999; 
>   
>   private Manager manager1 = null;
>   private Manager manager2 = null;
>   
>   @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);
>   }  
>   
>   private void insertDBRecords(){
>     manager1 = new Manager();
>     manager1.setName("Homer");
>     manager1.setDept("Sector 7-G");
>     
>     manager2 = new Manager();
>     manager2.setName("Carl");
>     manager2.setDept("Sector 7-G");
>     
>     //Fill the employees
>     try{
>       ExecutorService execService=Executors.newFixedThreadPool(2);
>       CompletionService<Manager> fillManagerSvc = new
> ExecutorCompletionService<Manager>(execService);
>             
>       fillManagerSvc.submit(new EmpCreator(manager1, NUM_EMPLOYEES));
>       fillManagerSvc.submit(new EmpCreator(manager2, NUM_EMPLOYEES));
>       for (int counter = 0; counter < 2; counter ++){
>         fillManagerSvc.take().get();
>       }
>     } catch (Exception e){
>       logger.error("Error occurred filling the manager with Employees");
>       System.exit(1);
>     }
>     
>     try{
>       ExecutorService execService2=Executors.newFixedThreadPool(2);
>       CompletionService<Manager> updateManagerSvc = new
> ExecutorCompletionService<Manager>(execService2);
>   
>       updateManagerSvc.submit(new ManagerUpdater(manager1));
>       updateManagerSvc.submit(new ManagerUpdater(manager2));
>       for (int counter = 0; counter < 2; counter++){
>         Manager manager = updateManagerSvc.take().get();
>         System.out.println("Manager " + manager.getName() + " filled");
>        }
>     } catch (Exception e){
>       logger.error("Error occurred updating the manager", e);
>       System.exit(1);      
>     }
>   }
>    
>   public static void main(String[] args){
>     EclipseLinkLazyTester lazyTester = new EclipseLinkLazyTester();
>     EclipseLinkLazyTester.initialize();
>     lazyTester.insertDBRecords();
>   }
>   
>   @Before
>   public void beforeTest(){
>     try {
>       manager1 = this.findManagerByName("Homer", "Sector 7-G");
>       manager2 = this.findManagerByName("Carl", "Sector 7-G");
>       System.out.println(manager1.getName() + "'s id = " +
> manager1.getManagerId());
>       System.out.println(manager2.getName() + "'s id = " +
> manager2.getManagerId());
>     } catch (Exception e) {
>       e.printStackTrace();
>       System.exit(1);
>     }
>   }
>   
>   @Test
>   public void testGetEmployeesLazy(){
>     try{
>       Department dept1 = new Department(manager1);
>       Department dept2 = new Department(manager2);
>       
>       updateDept(dept1);
>       updateDept(dept2);
>       
>       ExecutorService
> updateManagerService=Executors.newFixedThreadPool(2);
>       CompletionService<Manager> updateMgrSvc = new
> ExecutorCompletionService<Manager>(updateManagerService);
>       
>       ExecutorService execService=Executors.newFixedThreadPool(2);
>       CompletionService<Manager> ecs = new
> ExecutorCompletionService<Manager>(execService);
>       ecs.submit(new EmpRetriever(manager1, 0));
>       ecs.submit(new EmpRetriever(manager2, 0));
>       for (int counter = 0; counter < 2; counter++){
>         Manager mgr = ecs.take().get();
>         logger.info("Got employees for " + mgr.getName());
>         updateMgrSvc.submit(new ManagerUpdater(mgr));
>       }
>       for (int counter = 0; counter < 2; counter++){
>         Manager updatedManager = updateMgrSvc.take().get();
>         logger.info("Manager " + updatedManager.getName() + " updated.");
>       }
>     }catch (Exception e){
>       logger.error("Error occurred",e);
>     }
>   }
>   
>   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;
>   }
>    
>   @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 manager in same session = " +
> manager.getEmployees().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());
>   }
>   
>   private Manager getManager(Integer managerId){
>     EntityManager em = getNewEntityManager();
>     try {
>       return em.find(Manager.class, managerId);
>     } finally {
>       em.close();
>     }
>   }
>   
>   private Department updateDept(Department dept){
>     EntityManager em = getNewEntityManager();
>     try {
>       em.getTransaction().begin();
>       dept = em.merge(dept);
>       em.getTransaction().commit();
>     } finally {
>       em.close();
>     }
>     //The dept 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.getDepartment(dept.getDeptId());
>   }
>  
>   private Department getDepartment(Integer deptId){
>     EntityManager em = getNewEntityManager();
>     try {
>       return em.find(Department.class, deptId);
>     } finally {
>       em.close();
>     }
>   }  
>  
>   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=<dburl>;
>       dataSource.setURL(dbURL);
>       logger.info("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);
>     }
>   }
>   
>   class EmpRetriever implements Callable<Manager>{
>     private Manager manager;    
>     private long delay;
>     
>     public EmpRetriever(Manager manager, long delay) {
>       super();
>       this.manager = manager;
>       this.delay = delay;
>     }
> 
>     public Manager call() {
>       try {Thread.sleep(delay);} catch (InterruptedException e) {}
>       logger.info("Thread for getting Employees for manager " +
> manager.getName() + " starting");
>       logger.info("Manager " + manager.getName() + " has " +
> manager.getEmployees().size() + " employees");
>       return manager;
>     }    
>   }
>     
>   class ManagerUpdater implements Callable<Manager>{
>     private Manager manager;    
>     private long delay;
>     
>     public ManagerUpdater(Manager manager, long delay) {
>       this.manager = manager;
>       this.delay = delay;
>     }
> 
>     public ManagerUpdater(Manager manager) {
>       this(manager, 0);
>     }
> 
>     public Manager call() {
>       try {Thread.sleep(delay);} catch (InterruptedException e) {}
>       logger.info("Thread for updating manager " + manager.getName() + "
> starting");
>       manager = updateManager(manager);
>       logger.info("Manager " + manager.getName() + " has " +
> manager.getEmployees().size() + " employees");
>       return manager;
>     }
>   }
>   
>   class EmpCreator implements Callable<Manager>{
>     private Manager manager;
>     private long numEmployees;
>     
>     public EmpCreator(Manager manager, long numEmployees) {
>       this.manager = manager;
>       this.numEmployees = numEmployees;
>     }
>     
>     public Manager call(){
>       for (int counter = 0; counter < numEmployees; counter++){
>         manager.getEmployees().add(createEmployee(manager, "Emp_" +
> manager.getName() + "-" + counter, System.currentTimeMillis()));
>       }
>       return manager;
>     }
>   }
> }
> 
> ----------------------------Manager class
> ----------------------------------------
> @Entity
> @Table(name = "Manager")
> @NamedQuery(name = "GetManagerByNameDept", query = "SELECT obj FROM
> Manager obj WHERE obj.name = :name and obj.dept = :dept")
> public class Manager {
>   
>   @Id
>   @Column(name = "managerId")
>     @TableGenerator(
>         name="MGR_ID_SEQ",
>         table="SEQUENCE",
>         allocationSize=1)
>   @GeneratedValue(strategy = GenerationType.AUTO, generator =
> "MGR_ID_SEQ")
>   private Integer managerId;
>   
>   private String name = null;
>   
>   private String dept = null;
>   
>   //Changing the FetchType to EAGER avoids the issue.
>   @OneToMany (cascade=CascadeType.ALL, mappedBy="manager",
> fetch=FetchType.LAZY)
>   @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;
>   }   
> }
> 
> ----------------------------Employee class
> ----------------------------------------
> @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 String toString() {
>     return name;
>   }  
> }
> 
> ----------------------------Persistence.xml class
> ----------------------------------------
> <?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>
>       <class>Department</class>
> 
> 	  <!-- Provider-specific settings -->
> 	  <properties>
> 		  <property name="eclipselink.ddl-generation"
> value="drop-and-create-tables"/> <!-- other values are:
> create-tables|drop-and-create-tables|none -->
> 		  
> 		  <property name="eclipselink.logging.logger"
> value="Log4JEclipseLinkLogger"/>
> 		  <property name="eclipselink.logging.level" value="FINEST" /><!--
> http://www.oracle.com/technology/products/ias/toplink/jpa/howto/configure-logging.html
> -->
> 		  <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" />
> <!-- Full | HeadWeak | NONE | SoftWeak|Weak
> http://www.oracle.com/technology/products/ias/toplink/JPA/essentials/toplink-jpa-extensions.html#TopLinkCaching
> -->
> 		  <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"/>
> 		  <property name="eclipselink.cache.size.Employee" value="250000" />
> 	  </properties>  		
> 	</persistence-unit>
> </persistence>
> 
> 
> To get the error I simply run the project (NOT as a junit test - the
> project never gets that far).  Running it as an application invokes the
> main, which tries to insert the DB records (which it does successfully),
> but I get an error when it returns the count of the Employee records for a
> manager on the following line:
>       logger.info("Manager " + manager.getName() + " has " +
> manager.getEmployees().size() + " employees");
> The error we get is below:
>      Caused by: Exception [EclipseLink-2007] (Eclipse Persistence Services
> - 1.1.0.r3634):  
>      org.eclipse.persistence.exceptions.ConcurrencyException
>      Exception Description: Max number of attempts to lock object:
> Emp_Homer-55086 exceded.  Failed to clone the object.
> 
> After playing with the test project, I have the following findings:
> 1). This issue can only be reproduced if the employees have a FetchType of
> LAZY.  Changing them to EAGER makes the test project work.
> 2). Reducing the size of NUM_EMPLOYEES can also make the test project
> work.  Through trial and error I found that if I set the NUM_EMPLOYEES to
> 59,999 the test project works.  If I set it to 60,000 it fails.  Not sure
> if this is significant, but I found it interesting if nothing else.
> 
> Any ideas as to what might be causing this?  Thanks in advance.
> Kevin
> 


-----
---
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/Error-pulling-back-records-with-FetchType%3DLAZY-tp22957109p22973061.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.



Back to the top