Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eclipselink entity mappings cache

I am using EclipseLink for my project. I extend XMLMetadataSource (to provide a custom class loader) because entities I persist are runtime created. And it works OK.

I am getting "unknown entity type" when I do following.

  • Create entity

  • Create mapping

  • Create entity manager factory, provide custom class loader

  • create entity manager and persist. -- IT WORKS OK.

  • now drop entity , and drop from class loader

  • create same entity ,

  • create mapping again (of course it looks same)

  • try to refresh entity manager factory with new properties (new class loader, mapping file)

  • try to persist - complains "unknown type"

Any idea, if EL caches XML mappings. I tried to re-creating factory again but its same error.

I am tried MySQL and Derby. with 'drop-and-create-tables' and 'create-or-extend-tables' .

same result.

like image 598
PGK Avatar asked Dec 01 '25 04:12

PGK


1 Answers

Its not a bug per say in EL. But problem is with EL "not building or re-creating 'class-->class_descriptor' map (an internal map that holds Class object of each entity and entities description). I found this accidentally. For those interested, here is a sample code that might help.

public class Test1 {
public Test1(String pu, Map<String, Object> props ) {
    pu_name = pu;        
    properties = new HashMap<String, Object> ();
    properties.putAll(props);
    loader = new MyClassLoader();
}
public void initialization( ) {                
    mms = new WAMetadataSource();
    properties.put(PersistenceUnitProperties.METADATA_SOURCE, mms);
    properties.put(PersistenceUnitProperties.CLASSLOADER,loader);

    if(emf == null || !emf.isOpen()) {
        synchronized(Test1.class) {
            if (emf == null || !emf.isOpen()) {
                emf = Persistence.createEntityManagerFactory(pu_name, properties);                    
            } 
        }            
    } else {
                JpaHelper.getEntityManagerFactory(emf).refreshMetadata(properties);        
    }
    System.out.println("======> refreshed. emf.hascode : " + emf.hashCode() + ", loader.h : " + loader.hashCode());
}    public EntityManager getEntityManager(Map<String, Object> props) {
    if (em == null) {
        em = emf.createEntityManager(props);
    }

    return em;
} public void persist(Object obj) {
    try {
        getEntityManager(properties);
        System.out.println("===> em.hascode =" + em.hashCode() +", " +  JpaHelper.getEntityManager(em).getProperties().get(PersistenceUnitProperties.CLASSLOADER).hashCode() );
        em.clear();
        em.getTransaction().begin();
        em.persist(obj);
        em.getTransaction().commit();
    } finally {
    }    
}public Object getRuntimeEntityObject(int ii) {
        Object obj=null;
        Class clazz = loader.loadClass("com.xxx.sample.entity.runtime.User");
        if(ii == 1){
            obj = clazz.getConstructor(String.class).newInstance("Jai Ramjiki-1");                
        } else {
            obj = clazz.getConstructor(String.class).newInstance("Jai Ramjiki-2");
        }
        obj = clazz.cast(obj);
        return obj;
}public static void main(String[] args) {
    Map<String, Object> props = new HashMap<String, Object>();
    props.put(PersistenceUnitProperties.JDBC_DRIVER, "com.mysql.jdbc.Driver");
    props.put(PersistenceUnitProperties.JDBC_URL, "jdbc:mysql://localhost:3306/test" );
    props.put(PersistenceUnitProperties.JDBC_USER, "root");
    props.put(PersistenceUnitProperties.JDBC_PASSWORD, "root");
    props.put(PersistenceUnitProperties.DDL_GENERATION, "create-or-extend-tables");

    Test1 t1 = new Test1("mysql", props);         
    Object obj1 = t1.getRuntimeEntityObject(1);     
    System.out.println(" ****> obj1 = " + obj1 + ", classloader hashcode : " + obj1.getClass().getClassLoader().hashCode() );
    t1.initialization();
    t1.persist(obj1);
    System.out.println("Class 1 : " + obj1.getClass().hashCode() + ", obj1 : " + obj1);
    t1.close();

    // now drop the previous class loader and rerun same. 

    Test1 t2 = new Test1("mysql", props);
    Object obj2 = t2.getRuntimeEntityObject(2);
    System.out.println(" ****> obj2 = " + obj2 + ", classloader hashcode : " + obj2.getClass().getClassLoader().hashCode() );
    t2.initialization();
    t2.persist(obj2);
    t2.close();


    Object obj3 = t1.getRuntimeEntityObject(1);
    System.out.println(" ****> obj3 = " + obj3 + ", classloader hashcode : " + obj3.getClass().getClassLoader().hashCode() );
    t1.persist(obj3);


}

AND extend XMLMetadatSource

 @Override
public XMLEntityMappings getEntityMappings(Map<String, Object> properties, ClassLoader classLoader, SessionLog log) {
    properties.put(PersistenceUnitProperties.METADATA_SOURCE_XML_FILE, "eclipselink-orm-user.xml");
    properties.put(PersistenceUnitProperties.VALIDATOR_FACTORY, null);
    return super.getEntityMappings(properties, classLoader, log);
}

And create a runtime class using javassist in your CustomClassloader which extends ClassLoader

    public void createRuntimeClass(String className) throws Exception {        
    CtClass bclass = pool.makeClass(className);
    bclass.addConstructor(CtNewConstructor.defaultConstructor(bclass));        
    Map<String, String> fields = new HashMap<String, String>(); 
    addFields(fields); 
    int noOfFields = fields.size(); 
    CtClass[] fclasses = new CtClass[noOfFields];

    int ii=0;
    for (Entry<String, String> field : fields.entrySet()) {
        String fieldName = field.getKey();
        String fieldType = field.getValue();
        //.. code to add field
        bclass.addField(bfield);            
        //add getter method.
        // add getter and setters
    }        
    CtConstructor userConstructor = CtNewConstructor.make(constructorSource, bclass);
    bclass.addConstructor(userConstructor);
    byte bytes [] = bclass.toBytecode();        
    Class cls = bclass.toClass(this, null);
    loadedClasses.put(className, cls);
    loadClassBytes.put(className, bytes);    
}

and Override loadClass and getResourceAsStream methods.

public Class<?> loadClass(String name) throws ClassNotFoundException {return clazz = loadedClasses.get(name);}

public InputStream getResourceAsStream(String name) {return loadClassBytes.get(className);}

hope this helps

EL has provided a way to clear the cache of current project, and setting descriptors maps. but none of them worked. Not sure it is intended behavior or by mistake they exposed that API.

Gopi

like image 161
PGK Avatar answered Dec 04 '25 22:12

PGK



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!