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是必须的。
相关推荐
使用Morphia框架操作mongodb
morphia-0.99.jar 最新版本。
这个压缩文件包含mongo-2.7.3.jar和morphia-0.99.jar是morphia框架必须的jar
使用update方法更新带有@Reference注解的集合字段; 查询包含指定依赖的集合; 对获取的依赖集合添加限制; Morphia获取references的实现; 使用类似关系型数据库的join操作
morphia基于mongodb的基本开发
Mongo的ORM框架的学习Morphia
morphia-1.3.2.jar
语态参考示例该教程的源代码可从获得 显示如何在实体中使用@Reference批注。
mongo-2.7.3.jar和morphia-0.99.jar
spring MVC morphia mongo 整合的例子 网上下载的例子 自己调试保证绝对能运行
Morphia操作MongoDB,进行增删查改操作,内附详细代码。
morphia mongo db OR-mapping mongo db再带的CRUD 太麻烦了, 一个不错的框架 类似 Hibernate
NULL 博文链接:https://gaozzsoft.iteye.com/blog/1460827
NULL 博文链接:https://hogwartsrow.iteye.com/blog/1471846
后续提交放在https://github.com/zdsiyan/watermelon 上, 用eclipse导入该工程需安装m2eclipse,jetty等查件. 另外.settings下的org.eclipse.wst.common.component文件如下: ...</project-modules>
本文解释了在文档和对象之间进行映射的好处,并演示了如何使用Morphia来实现这个功能。然后演示了如何持久保存、加载、删除和查询映射到 MongoDB的Java域模型。MongoDB是面向文档的数据库,用于存储并检索类似...
Morphia开发简介.pdf
Morphia演示 这是一个简单的测试项目,用于显示一些Morphia功能。要求JDK 7+ Gradle您可以在localhost:27017上使用独立的MongoDB服务器,或者如果该服务器不可用,则测试代码将启动嵌入式MongoDB进程。入门在基本...
PlayMorphia模块一个功能强大且易于使用的框架,可让访问您的Play!framework应用程序。 该模块建立在著名的。 秉承Play!Framework的理念,PlayMorphia提供了数据层基础结构来帮助开发人员专注于业务逻辑而不是...
Morphia/Jackson Java 8 可选示例Morphia (mongoDB) 和 Jackson (JSON) 序列化都可以用来序列化/反序列化新的 Java 8 Optional 类型。 对于 Jackson,您只需要注册 JDK8 模块。 对于 Morphia,您需要为 Optional ...