JPA dev: 几个问题总结(续)

本文探讨了ORM框架中实体状态管理的关键概念,包括由PoJo类生成数据库表的方法、FetchTypeLazy与Eager的区别及应用、常见错误的解决办法,并介绍了如何通过Java反射将代理对象转换为原始对象。

2. 如何由PoJo类生成数据库中的表

首先可以根据实体间关系及对应关系设计表,写出相应建表SQL语句,执行生成表,或者可以用hibernate映射来生成表,在http://kylinsoong.iteye.com/admin/blogs/739502一开始说明怎样通过hibernate mapping生成表;

我们例子生成表如下图所示:



 
 共生成11张表,7个实体对应7张表及5个一对多关联表(User和Event,User和Friend,Event和Property,Wife和Pet,Pet和Property)。

 

3. FetchType Lazy及Eager测试

一对一下FetchType Lazy

设定User和UserCard,Friend和UserCard,Wife和UserCard对应FetchType 设为lazy,User和Wife设为Eager,运行如下代码;

public static void main(String[] args) throws Throwable {
		EntityManagerFactory emf = JPAUtil.createEMF("oracle");
		EntityManager em = emf.createEntityManager();
		EntityTransaction t = em.getTransaction();
		t.begin();
		
		User user = em.find(User.class, new Long(1));
		System.out.println(user.getFriends().get(0).getUserCard().getClass());
		System.out.println(user.getUserCard().getClass());
		System.out.println(user.getWife().getClass());
		System.out.println(user.getWife().getUserCard().getClass());
		
		t.commit();
		em.close();
		emf.close();
	}

 运行结果:

class com.tibco.hibernate.po.UserCard_$$_javassist_0
class com.tibco.hibernate.po.UserCard_$$_javassist_0
class com.tibco.hibernate.po.Wife
class com.tibco.hibernate.po.UserCard_$$_javassist_0

 从结果可以看到一对一下FetchType 设为Lazy,对应熟悉值为该属性的代理,当然设为Eager后就不为代理,下面代码通过反射对代理类进行研究

public static void main(String[] args) throws Throwable {
		EntityManagerFactory emf = JPAUtil.createEMF("oracle");
		EntityManager em = emf.createEntityManager();
		EntityTransaction t = em.getTransaction();
		t.begin();
		
		User user = em.find(User.class, new Long(1));
		showProxyClass(user.getUserCard());
		
		t.commit();
		em.close();
		emf.close();
	}


	private static void showProxyClass(Object bean) throws Throwable {
		PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
		for(PropertyDescriptor propertyDescriptor : propertyDescriptors) {
			System.out.println(propertyDescriptor.getDisplayName() + "   " + propertyDescriptor.getReadMethod());
		}
	}

 

运行结果:

cardNumber   public final java.lang.String com.tibco.hibernate.po.UserCard_$$_javassist_0.getCardNumber()
class   public final native java.lang.Class java.lang.Object.getClass()
handler   null
hibernateLazyInitializer   public final org.hibernate.proxy.LazyInitializer com.tibco.hibernate.po.UserCard_$$_javassist_0.getHibernateLazyInitializer()
id   public final java.lang.Long com.tibco.hibernate.po.UserCard_$$_javassist_0.getId()

 

      可以看出除去CardNumber属性cardNumber  ,id 外,又多出了handler和hibernateLazyInitializer属性,且cardNumber  ,id 对应的Get方法不是getId()和getCardNumber(),而是UserCard_$$_javassist_0.getId()和UserCard_$$_javassist_0.getCardNumber(),是其代理类中的Get方法;继续研究.UserCard_$$_javassist_0类:

private static void showProxyClass(Object bean) throws Throwable {
		PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
		for(PropertyDescriptor propertyDescriptor : propertyDescriptors) {
			if(propertyDescriptor.getDisplayName().compareTo("hibernateLazyInitializer")== 0) {
				Class c =  Class.forName("com.tibco.hibernate.po.UserCard_$$_javassist_0");
				for(Class cl :c.getInterfaces()) {
					System.out.println(cl);
				}
				for(Method m : c.getMethods()) {
					if(m.getName().startsWith("get")){
						System.out.println(m.getName());
					}
				}
				Object obj = propertyDescriptor.getReadMethod().invoke(bean, new Object[]{});
				System.out.println(obj.getClass());
			}
		}
	}

 运行结果:

interface org.hibernate.proxy.HibernateProxy
interface javassist.util.proxy.ProxyObject
getHibernateLazyInitializer
getCardNumber
getId
getClass
class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer

 看到UserCard_$$_javassist_0实现了两个接口getHibernateLazyInitializer()方法可以获取

一对多下FetchType Lazy

User和Event,Event和Property,Wife和Pet,Pet和Property FetchType为Lazy,User和Friend FetchType为Eager,运行如下代码

public static void main(String[] args) throws Throwable {
		EntityManagerFactory emf = JPAUtil.createEMF("oracle");
		EntityManager em = emf.createEntityManager();
		EntityTransaction t = em.getTransaction();
		t.begin();

		User user = em.find(User.class, new Long(1));
		System.out.println(user.getEvents().getClass());
		System.out.println(user.getFriends().getClass());
		System.out.println(user.getEvents().get(0).getProperties().getClass());
		System.out.println(user.getWife().getPets().getClass());
		System.out.println(user.getWife().getPets().get(0).getProperties().getClass());
		
		t.commit();
		em.close();
		emf.close();
	}

运行结果:

class org.hibernate.collection.PersistentBag
class org.hibernate.collection.PersistentBag
class org.hibernate.collection.PersistentBag
class org.hibernate.collection.PersistentBag
class org.hibernate.collection.PersistentBag

发现一对多和一对一以Lazy的处理方式是不一样的。

说明一个常见错误:

上述例子中总共有User和Event,User和Friend,Event和Property,Wife和Pet,Pet和Property 5对一对多关系,已经将User和Friend FetchType为Eager,此时在将剩余4对其中任何一项设为Eager,运行程序,会出现“Unable to build EntityManagerFactory”错误,错误的根本原因是“cannot simultaneously fetch multiple bags”

如下所示错误堆栈的打印输出:

Exception in thread "main" javax.persistence.PersistenceException: [PersistenceUnit: com.tibco.hibernate.po] Unable to build EntityManagerFactory
	at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:677)
	at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:126)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:52)
	at com.tibco.hibernate.jpa.JPAUtil.createEMF(JPAUtil.java:20)
	at com.tibco.hibernate.jpa.JPAClient.main(JPAClient.java:26)
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
	at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:94)
	at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:98)
	at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:66)
	at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:56)
	at org.hibernate.loader.entity.BatchingEntityLoader.createBatchingEntityLoader(BatchingEntityLoader.java:126)
	at org.hibernate.persister.entity.AbstractEntityPersister.createEntityLoader(AbstractEntityPersister.java:1765)
	at org.hibernate.persister.entity.AbstractEntityPersister.createEntityLoader(AbstractEntityPersister.java:1769)
	at org.hibernate.persister.entity.AbstractEntityPersister.createLoaders(AbstractEntityPersister.java:3002)
	at org.hibernate.persister.entity.AbstractEntityPersister.postInstantiate(AbstractEntityPersister.java:2995)
	at org.hibernate.persister.entity.SingleTableEntityPersister.postInstantiate(SingleTableEntityPersister.java:712)
	at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:329)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1341)
	at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)
	at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:669)
	... 4 more

错误的原因是:如果一个实体里要映射多个集合实体时,我们不能把两个集合的的FetchType设置为EAGER,此时只能设置为LAZY,否则会报:cannot simultaneously fetch multiple bags。

解决办法:将一个FetchType设置为Lazy

 

4 Lazy和Eager的主要区别在于效率,当效率要求高时必须用Lazy,但有些时候既要要求高效率(Lazy),但相应Lazy标记的属性不要是相关的代理,这时就需要把相关属性代理转换成原属性,下面是一个实现这个功能的类,主要思想是Java反射:

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @author Kylin Soong
 * 
 */
public class POPropertyUtil {

	public static Object setBeanProperty(Object bean) throws Throwable  {
		PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
		for(PropertyDescriptor propertyDescriptor : propertyDescriptors) {
			if(propertyDescriptor.getReadMethod() == null) {
				continue;
			}
			Object obj = propertyDescriptor.getReadMethod().invoke(bean, new Object[]{});
			if(obj == null) {
				continue;
			}
			if(obj.getClass().getPackage().getName().compareTo("com.staffware.frameworks.po") == 0) {
				setBeanProperty(obj);
			}
			if(obj instanceof List) {
				List lists = (List) obj; 
				for(Object o : lists) {
					setBeanProperty(o);
				}
			}
			if(isProxyProperty(obj)) {
				if(propertyDescriptor.getWriteMethod() != null) {
					Object invokeObj = clone(obj, propertyDescriptor);
					propertyDescriptor.getWriteMethod().invoke(bean, new Object[]{invokeObj});
				} else {
					throw new RuntimeException(propertyDescriptor.getDisplayName() + "has a null getWriteMethod");
				}
			}
		}
		return bean;
	}

	private static Object clone(Object obj, PropertyDescriptor propertyDescriptor) throws Throwable {
		if(obj instanceof List) {
			List lists = (List) obj;
			List newList = new ArrayList();
			for(Object o : lists) {
				newList.add(o);
			}
			return newList;
		} else {
			Object newObj = propertyDescriptor.getPropertyType().newInstance();
			return replication(newObj, obj);
			
		}
	}

	private static void invokeCollectionEntity(Object o) throws Throwable {
		PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(o.getClass()).getPropertyDescriptors();
		for(PropertyDescriptor propertyDescriptor : propertyDescriptors) {
			if(propertyDescriptor.getReadMethod() != null) {
				propertyDescriptor.getReadMethod().invoke(o, new Object[]{});
			}
		}
	}

	private static Object replication(Object newObj, Object oldObj) throws Throwable {
		PropertyDescriptor[] newPropertyDescriptors = Introspector.getBeanInfo(newObj.getClass()).getPropertyDescriptors();
		PropertyDescriptor[] oldPropertyDescriptors = Introspector.getBeanInfo(oldObj.getClass()).getPropertyDescriptors();
		for(PropertyDescriptor propertyDescriptor : newPropertyDescriptors) {
			if(propertyDescriptor.getWriteMethod() != null) {
				Method readMethod = getReadMethod(propertyDescriptor.getDisplayName(), oldPropertyDescriptors);
				propertyDescriptor.getWriteMethod().invoke(newObj, new Object[]{readMethod.invoke(oldObj, new Object[]{})});
			}
		}
		return newObj;
	}

	private static Method getReadMethod(String displayName,
			PropertyDescriptor[] oldPropertyDescriptors) {
		for(PropertyDescriptor propertyDescriptor : oldPropertyDescriptors) {
			if(propertyDescriptor.getDisplayName().compareTo(displayName) == 0) {
				if(propertyDescriptor.getReadMethod() == null){
					throw new RuntimeException("Get Read method return a null value: " + propertyDescriptor.getPropertyType() + " " + propertyDescriptor.getDisplayName());
				}
				return propertyDescriptor.getReadMethod();
			}
		}
		throw new RuntimeException("can not find read method in:" + oldPropertyDescriptors);
	}

	private static boolean isProxyProperty(Object obj) throws Throwable {
		String name = obj.getClass().getName();
		return name.contains("_$$_javassist_") || name.contains("org.hibernate.collection.PersistentBag");
	}
}

 

对上述类进行测试,如下代码:

public static void main(String[] args) throws Throwable {
		EntityManagerFactory emf = JPAUtil.createEMF("oracle");
		EntityManager em = emf.createEntityManager();
		EntityTransaction t = em.getTransaction();
		t.begin();

		User user = em.find(User.class, new Long(1));
		System.out.println(user.getEvents().getClass());
		System.out.println(user.getUserCard().getClass());
		System.out.println(user.getWife().getClass());
		POPropertyUtil.setBeanProperty(user);
		System.out.println(user.getEvents().getClass());
		System.out.println(user.getUserCard().getClass());
		System.out.println(user.getWife().getClass());
		
//		t.commit();
		em.close();
		emf.close();
	}

 

运行结果:

class org.hibernate.collection.PersistentBag
class com.tibco.hibernate.po.UserCard_$$_javassist_0
class com.tibco.hibernate.po.Wife_$$_javassist_3
class java.util.ArrayList
class com.tibco.hibernate.po.UserCard
class com.tibco.hibernate.po.Wife

 

既将相应的代理还原了;

 

5 说明一个常见的错误:“detached entity passed to persist”,先还原错误:

 如上在获取User对象后给其设定ID值,然后保存如下代码

public static void main(String[] args) throws Throwable {
		EntityManagerFactory emf = JPAUtil.createEMF("oracle");
		EntityManager em = emf.createEntityManager();
		EntityTransaction t = em.getTransaction();
		t.begin();

		User user = JPAClient.getUser();
		user.setId(new Long(1212));
		em.persist(user);
		
		t.commit();
		em.close();
		emf.close();
	}

 运行代码抛出异常,异常堆栈信息如下:

Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.tibco.hibernate.po.User
	at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
	at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:226)
	at com.tibco.hibernate.jpa.JPAClient.main(JPAClient.java:33)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.tibco.hibernate.po.User
	at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:102)
	at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
	at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:646)
	at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:620)
	at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:624)
	at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220)

 错误原因是:实体的状态变化EntityManager是根据实体的ID来判断,一般一个实体Persist的过程就是给他分配一个ID的过程,当一个实体有唯一ID时,EntityManager就认为它已经处于游离(detached )状态,而游离态实例不能够持久化

 

 

 

【无人机】基于改进粒子群算法的无人机路径规划研究[和遗传算法、粒子群算法进行比较](Matlab代码实现)内容概要:本文围绕基于改进粒子群算法的无人机路径规划展开研究,重点探讨了在复杂环境中利用改进粒子群算法(PSO)实现无人机三维路径规划的方法,并将其与遗传算法(GA)、标准粒子群算法等传统优化算法进行对比分析。研究内容涵盖路径规划的多目标优化、避障策略、航路点约束以及算法收敛性和寻优能力的评估,所有实验均通过Matlab代码实现,提供了完整的仿真验证流程。文章还提到了多种智能优化算法在无人机路径规划中的应用比较,突出了改进PSO在收敛速度和全局寻优方面的优势。; 适合人群:具备一定Matlab编程基础和优化算法知识的研究生、科研人员及从事无人机路径规划、智能优化算法研究的相关技术人员。; 使用场景及目标:①用于无人机在复杂地形或动态环境下的三维路径规划仿真研究;②比较不同智能优化算法(如PSO、GA、蚁群算法、RRT等)在路径规划中的性能差异;③为多目标优化问题提供算法选型和改进思路。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注算法的参数设置、适应度函数设计及路径约束处理方式,同时可参考文中提到的多种算法对比思路,拓展到其他智能优化算法的研究与改进中。
标题中的"EthernetIP-master.zip"压缩文档涉及工业自动化领域的以太网通信协议EtherNet/IP。该协议由罗克韦尔自动化公司基于TCP/IP技术架构开发,已广泛应用于ControlLogix系列控制设备。该压缩包内可能封装了协议实现代码、技术文档或测试工具等核心组件。 根据描述信息判断,该资源主要用于验证EtherNet/IP通信功能,可能包含测试用例、参数配置模板及故障诊断方案。标签系统通过多种拼写形式强化了协议主题标识,其中"swimo6q"字段需结合具体应用场景才能准确定义其技术含义。 从文件结构分析,该压缩包采用主分支命名规范,符合开源项目管理的基本特征。解压后预期可获取以下技术资料: 1. 项目说明文档:阐述开发目标、环境配置要求及授权条款 2. 核心算法源码:采用工业级编程语言实现的通信协议栈 3. 参数配置文件:预设网络地址、通信端口等连接参数 4. 自动化测试套件:包含协议一致性验证和性能基准测试 5. 技术参考手册:详细说明API接口规范与集成方法 6. 应用示范程序:展示设备数据交换的标准流程 7. 工程构建脚本:支持跨平台编译和部署流程 8. 法律声明文件:明确知识产权归属及使用限制 该测试平台可用于构建协议仿真环境,验证工业控制器与现场设备间的数据交互可靠性。在正式部署前开展此类测试,能够有效识别系统兼容性问题,提升工程实施质量。建议用户在解压文件后优先查阅许可协议,严格遵循技术文档的操作指引,同时需具备EtherNet/IP协议栈的基础知识以深入理解通信机制。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值