我正在为我的项目使用EclipseLink。 我扩展XMLMetadataSource(以提供自定义类加载器),因为我持久化的实体是运行时创建的。它运作正常。
当我关注时,我会收到“未知实体类型”。
创建实体
创建映射
创建实体管理器工厂,提供自定义类加载器
创建实体经理并坚持。 - 它工作正常。
现在删除实体,并从类加载器中删除
创建相同的实体,
再次创建映射(当然它看起来一样)
尝试使用新属性刷新实体管理器工厂(新的类加载器,映射文件)
尝试坚持 - 抱怨“未知类型”
任何想法,如果EL缓存XML映射。 我试图再次重新创建工厂,但同样的错误。
我尝试了MySQL和Derby。使用'drop-and-create-tables'和'create-or-extend-tables'。
同样的结果。
答案 0 :(得分:2)
我用eclipse链接提交了一个错误。
答案 1 :(得分:1)
它不是EL中每个人说的错误。但是问题在于EL“没有构建或重新创建'类 - > class_descriptor'地图(一个内部地图,它包含每个实体的Class对象和实体描述)。我偶然发现了这一点。对于那些感兴趣的人,这里有一个示例代码可能会有所帮助。
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扩展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);
}
在CustomClassloader中使用javassist创建一个运行时类,它扩展了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);
}
并覆盖loadClass和getResourceAsStream方法。
public Class<?> loadClass(String name) throws ClassNotFoundException {return clazz = loadedClasses.get(name);}
public InputStream getResourceAsStream(String name) {return loadClassBytes.get(className);}
希望这会有所帮助
EL提供了一种清除当前项目缓存和设置描述符映射的方法。但他们都没有工作。不确定它是否是预期的行为或错误地暴露了该API。
戈皮
答案 2 :(得分:0)
是的,持久性单元加载只进行一次。如果使用XMLMetadataSource更改映射,则必须使用EMF上的refreshMetadata()告知工厂刷新其映射,如下所述: http://wiki.eclipse.org/EclipseLink/DesignDocs/340192#Refresh
之后,获得的下一个EntityManagers将使用新的映射,而现有的EM仍将使用旧的映射。