`
truemylife
  • 浏览: 228270 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论
阅读更多

       morphia是mongodb for java基础上的一个开源项目,但2011年之后几乎不在更新,非常不活跃,建议后来者谨慎选择。由于团队在比较早之前就选择了morphia,因此在未重构之前,还是尽可能的用好morphia。

morphia项目地址:

https://code.google.com/p/morphia/

这里只说morphia Reference使用的问题和使用建议

一、慎用Reference,合理使用lazy=true

@Reference有lazy属性,默认为false,当为false时,会自动加载关联的数据,跟hibernate类似。理论上Reference会引发死循环,最简单的业务模式,就是好友业务,用户A通过Reference加载关联的好友B,B通过Reference加载关联的好友A,就形成了死循环。因此在确定类似这样的业务或者业务复杂到开发人员不能掌控时,非常有必要把lazy=true。如果形成死循环,会造成链接池耗尽,请求被阻塞,占用较多的cpu,整个应用变的极慢。因此强列建议慎用@Reference,如果要用,一定要搞清楚业务,在没必要实时加载引用数据的情况下,务必加上lazy=true。

 

二、第三方依赖

@Reference(lazy=true)

设置lazy=true后,会发现需要cglib及proxytoys第三方包的支持,需要加入对应的jar包。

 

三、多个mongodb数据库,且采用spring配置时引出的Reference bug及解决办法

如果mongodb两个database,spring配置片段如下:

 

  

<bean id="mongouri" class="com.mongodb.MongoURI">
	 <constructor-arg value="${mongo.url}"/>
</bean>
<bean id="mongo" class="com.mongodb.Mongo">
	<constructor-arg><ref bean="mongouri"></ref></constructor-arg>
</bean>
<bean id="morphia" class="com.google.code.morphia.Morphia">
       <property name="mongo" ref="mongo"/>
       <property name="databaseName" value="cmac"/>
       <property name="user" value="${mongo.username}"/>
       <property name="password" value="${mongo.password}"/>
</bean>
<bean id="practisemorphia" class="com.google.code.morphia.Morphia">
	<property name="mongo" ref="mongo"/>
        <property name="databaseName" value="practise"/>
        <property name="user" value="${mongo.username}"/>
        <property name="password" value="${mongo.password}"/>
</bean>
<bean id="datastore" factory-bean="morphia" factory-method="getDatastore"/>
<bean id="practiseds" factory-bean="practisemorphia" factory-method="getDatastore"/>

 

       我们为两个database分别写了两个BaseDao,让两个BaseDao分别持有不同的DatastoreImpl,没有问题,但是在使用Reference且lazy=true时,问题来了。如上的配置,spring初始化时先初始化datastore,再初始化practiseds,应用起来后,发现practiseds相关的@Reference(lazy=true)操作都正常,而datastore相关的@Reference(lazy=true)不正常,报的错误是MappingException。通过查看源码,发现getDatastore调用DatastoreHolder时是静态且单例的模式,因此当初始化practiseds时,就把之前的DatastoreHolder的静态变量覆盖了。Reference在lazy=true的情况下,会从DatastoreHolder里取当前的DatatoreImpl,而此时取到的都只是practiseds的DatatoreImpl,因此datastore相关的@Reference(lazy=true)通通抛出MappingException异常。

此问题的修复,只要在Dao对应方法里,加上以下代码:

DatastoreHolder.getInstance().set(super.getDs());

 //在BaseDao里定义DatastoreImpl成员变量并注入@Resource(name = "datastore")

 

public DatastoreImpl getDs() {
		return ds;
	}

	@Resource(name = "datastore")
	public void setDs(DatastoreImpl ds) {
		this.ds = (DatastoreImpl)ds;
		initType(((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]));
		ensureIndexes();
	}

 

为了书写更加美观和便利,可以改造成AOP的方式

 

SupportReference.java

 

 

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SupportReference {}

 

 

AbstractMorphiaReferenceAspect.aj

 

 

public abstract aspect AbstractMorphiaReferenceAspect {

	public AbstractMorphiaReferenceAspect() {
	}
	
	@SuppressAjWarnings("adviceDidNotMatch")
	Object around(Object model) : supportReferenceMethodExecution(model) {
		@SuppressWarnings("rawtypes")
		Class clazz = model.getClass().getSuperclass();
		try{
		@SuppressWarnings("unchecked")
		Method ds = clazz.getDeclaredMethod("getDs");
		DatastoreImpl datastoreimpl =(DatastoreImpl)ds.invoke(model);
		DatastoreHolder.getInstance().set(datastoreimpl);
		}catch(Exception ex){ex.printStackTrace();}
		Object result = proceed(model);
		return result;
	}
	
	
	protected abstract pointcut supportReferenceMethodExecution(Object model);
}

 

 

MorphiaReferenceAspect.aj

 

 

public aspect MorphiaReferenceAspect extends AbstractMorphiaReferenceAspect {
	public MorphiaReferenceAspect() {
		super();
	}

	protected pointcut supportReferenceMethodExecution(Object model) : 
		(execution(* com.hx.corebiz.*.dao.impl.*.*(..))) && 
				@annotation(SupportReference) &&
				this(model);
	
}

 

 

 aop.xml

 

<?xml version="1.0" encoding="UTF-8" ?>
<aspectj>
	<aspects>
	<aspect name="com.hx.corebase.aop.morphiareference.MorphiaReferenceAspect" /> 
	</aspects>
	<weaver options="-verbose">
		<include within="com.hx.corebiz.*.dao.impl.*" />
        </weaver>
</aspectj>

 

在Dao类里,在需要使用@Reference(lazy=true)方法的加上Annotation @SupportReference即可

 

 四、ignoreMisssing

ignoreMisssing会捕获Reference所有异常,如果不想看到一些烦人的异常,并让程序继续运行,ignoreMisssing=true是有必要的。比方说我们Reference到另一张表,某天另一张表的数据删除了,而开发人员也没有做关联数据的清理,此时如果ignoreMisssing=false(默认),就会抛出异常,这种情况下有必要指定ignoreMisssing=true。

 

五、Memcached与Reference

存入Memcached时,会对对象进行序列化,此时应注意以下三点:

1、如果你不想序列化对象里某成员,请使用transient关键字,这样可减小对象大小,节省内存空间,提高性能。

2、如果lazy=false,在序列化存入到memcache时,会加载出所有Reference对象。如果Reference是个大List<Object>,可以想像此java对象将会非常庞大。而memcache更适合小对象的缓存,默认在1M之内。不过这个策略跟总大小也有关系,本人设置memcached内存总大小为64M,放入一个大对象,报错信息page超过1M,但把总大小置成512M后,与之前一样没有设置PAGE大小(即参数-L),这时就不报错了,没有仔细研究memcache代码,但据此可判断此1M的限制,跟总大小有某种逻辑关系。当然如果光光为了消除1M限制的错误,可以通过设置-L,比如设置成5M,但这不是理想的解决办法。通过测试,memcached对一个大对象和一个小对象的存取,速度不在一个数量级上,因此如果真的有对象超过1M,最好对程序进行优化设计。像上述的情况Reference是个大List<Object>,此时把lazy=true,是非常有必要的。可大大减小对象的大小。

3、另外重复第一点,在互相引用的时候,此时如不加lazy=true,就会产生死循环,因此这种情况下lazy=true是必须的。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics