`
兩ting
  • 浏览: 78327 次
  • 性别: Icon_minigender_2
  • 来自: 成都
社区版块
存档分类
最新评论

mongodb的morphia框架笔记

阅读更多

 

快速开始:
@Entity
public class Hotel {
     @Id private ObjectId id;
    private String name;
    private int stars;
     @Embedded
    private Address address;
}
 
@Embedded
public class Address {
    private String street;
    private String city;
    private String postCode;
    private String country;
}
 
main方法:
Mongo mongo=new Mongo();
Morphia morphia = new Morphia();
morphia.map(Hotel.class).map(Address.class);
Datastore ds = morphia.createDatastore(mongo, "my_database");
//保存
ds.save(hotelToSave);
//查询四星级酒店
//List<Hotel> fourStarHotels = ds.find(Hotel.class, "stars >=", 4).asList();
List<Hotel> fourStarHotels = ds.find(Hotel.class).field("stars").greaterThanOrEq(4).asList();
 
-------------------------------------------------
Datastore创建后index和capped collections的初始化:
ds.ensureIndexes(); ///creates all defined with @Indexed
ds.ensureCaps(); //creates all collections for @Entity(cap=@CappedAt(...))
这两个方法应该在你把实体注册到morphia之后(morphia.map(Hotel.class).map(Address.class))来调用,它默认会同步的创建index和capped collections,这可以在每次系统启动时确保索引存在(包括第一次运行的时候)。
 
查询一条记录:
MyEntity e = ds.find(MyEntity.class).get(); //get the first one by type
MyEntity e = ds.find(MyEntity.class).field("name").equal("someName").get(); //get the first one where name = "someName"
 
-------------------------------------------------
Datastore接口介绍:
-------------------------
get方法:其实就是find方法的快捷方式,根据ID返回一个实体,或者返回null。
Hotel hotel = ds.get(Hotel.class, hotelId);
-------------------------
find方法:会返回一个Query对象,支持迭代器和QueryResults接口。
//use in a loop
for(Hotel hotel : ds.find(Hotel.class, "stars >", 3))
   print(hotel);
 
//get back as a list
List<Hotel> hotels = ds.find(Hotel.class, "stars >", 3).asList();
 
//sort the results
List<Hotel> hotels = ds.find(Hotel.class, "stars >", 3).sort("-stars").asList();
 
//get the first matching hotel, by querying with a limit(1)
Hotel gsHotel = ds.find(Hotel.class, "name", "Grand Sierra").get();
 
//same as
Hotel gsHotel = ds.find(Hotel.class, "name =", "Grand Sierra").get();
 
可用的操作符列表: ["=", "==","!=", "<>", ">", "<", ">=", "<=", "in", "nin", "all", "size", "exists"]
-------------------------
save方法:
Hotel hotel = new Hotel();
ds.save(hotel);
//@Id field is filled in for you (after the save), if you didn't set it.
ObjectId id = hotel.getId();
-------------------------
delete方法:根据一个ID或者查询对象来删除
ds.delete(Hotel.class, "Grand Sierra Resort");
//use a query
ds.delete(ds.createQuery(Hotel.class).filter("pendingDelete", true));
-------------------------
FindAndDelete方法:删除并返回记录的原子操作
Hotel grandSierra = ds.findAndDelete(ds.get(Hotel.class, "Grand Sierra Resort"));
-------------------------
Update方法:更新部分信息时比整体save一次要高效的多,例如更新用户的最后登录时间:
@Entity
class User
{
   @Id private ObjectId id;
   private long lastLogin;
   //... other members
 
   private Query<User> queryToFindMe()
   {
      return datastore.createQuery(User.class).field(Mapper.ID_KEY).equal(id);
   }
 
   public void loggedIn()
   {
      long now = System.currentTimeMillis();
      UpdateOperations<User> ops = datastore.createUpdateOperations(User.class).set("lastLogin", now);
      ds.update(queryToFindMe(), ops);
      lastLogin = now;
   }
}
 
有关update方法的更多高级操作:
http://code.google.com/p/morphia/wiki/Updating
-------------------------------------------------
Query接口介绍:
可以添加查询条件,排序,限定返回结果条数和位置。实现了QueryResults接口。
-------------------------
Filter方法:
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12);
第一个参数是属性名和比较符,第二个参数是比较值。
多个fileter直接的关系是“与(and)”
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).filter("foo <", 30);
可用操作符列表:
["=", "==","!=", "<>", ">", "<", ">=", "<=", "in", "nin", "all", "size", "exists"]
-------------------------
Fluent接口:
与Fileter接口差不多,英文呢方法名,更符合使用习惯:
Query q = ds.createQuery(MyEntity.class).field("foo").equal(1);
可用方法列表:
[exists,doesNotExist,greaterThan,greaterThanOrEq,lessThan,lessThanOrEq,equal,notEqual,hasThisOne,hasAllOf,hasAnyOf,hasNoneOf,hasThisElement,sizeEq]
 
使用Fluent接口,可以使用or条件:
Query<Person> q = ad.createQuery(Person.class);
q.or(
        q.criteria("firstName").equal("scott"),
        q.criteria("lastName").equal("scott")
);
-------------------------
Geo-spatial:mongoDB的地理位置操作
-------------------------
Fields方法:
类似与mongo本身的query,可以用“点号”指定属性。
Query q = ds.createQuery(Person.class).field("addresses.city").equal("San Francisco");
//or with filter, or with this helper method
Query q = ds.find(Person.class, "addresses.city", "San Francisco");
-------------------------
Validation:如果查询时属性名找不到,则会抛出异常。如果用到“点号”指定属性,则表达式每一部分都必须匹配类的结构图。
如果服务器可以强制转换数据,则数据类型的错误会被作为警告记录下来。
上述校验特性可以在任何一个查询或查询的一部分处被关闭:
Query q = ds.createQuery(MyEntity.class).disableValidation();
//or it can be disabled for just one filter
Query q = ds.createQuery(MyEntity.class).disableValidation().filter("someOldField", value).enableValidation().filter("realField", otherVal);
-------------------------
Sort方法:排序
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).order("dateAdded");
... // desc order
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).order("-dateAdded");
... // asc dateAdded, desc foo
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).order("dateAdded, -foo");
-------------------------
Limit方法:
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).limit(100);
-------------------------
offset(skip)方法:要求服务器跳过一部分记录,这么做效率不如使用一些属性的range filter,比如pagination。
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).offset(1000);
-------------------------
只返回指定的属性:这么做会导致不完整的对象,小心使用。
MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(true, "foo").get();
val = e.getFoo(); // 仅返回这个字段
 
MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(false, "foo").get();
val = e.getFoo(); // 仅不返回这个字段
 
指定字段名的参数可以是字符串list或者数组:
MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(true, "foo", "bar").get();
-------------------------
取数据:
使用QueryResults接口的方法即可取到数据,这些方法不会影响Query对象本身(返回的是副本),所以可以反复调用。
get()     使用limit(1),返回一条记录
asList()     返回所有记录,大数据量时可能较慢
fetch()     明确要求返回一个可迭代的对象
asKeyList()     返回记录的Key<T>,只从server取id字段
fetchEmptyEntities()     与fetch类似,但只取id字段
 
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12);
MyEntity e = q.get();
e = q.sort("foo").get();
for (MyEntity e : q)  print(e);
List<MyEntity> entities = q.asList();
-------------------------------------------------
生命周期方法注解:
@PrePersist - 在save之前调用,可以返回一个DBObject以替代一个空值
@PostPersist - 在save之后调用
@PreLoad - 在将数据映射到POJO之前调用,DBObject作为参数传入,你可以手动修改其中的值
@PostLoad - 在将数据映射到POJO之后调用
 
测试类例子:
http://code.google.com/p/morphia/source/browse/trunk/morphia/src/test/java/com/google/code/morphia/TestDatastore.java
 
//这个例子保存前将最后登录时间更新为当前时间
class BankAccount {
  @Id String id;
  Date lastUpdated = new Date();
  @PrePersist void prePersist() {lastUpdated = new Date();}
}
 
下面的这个例子中,通过EntityListerners注解,将生命周期事件的实现放到外部类当中:
@EntityListeners(BackAccountWatcher.class)
class BankAccount {
  @Id String id;
  Date lastUpdated = new Date();
}
 
class BankAccountWatcher{
  @PrePersist void prePersist(BankAccount act) {act.lastUpdated = new Date();}
}
 
注意:没有delete操作相关的生命周期事件。
-------------------------------------------------
类注解:
-------------------------
@Entity
标记一个要被映射的类。此注解为可选,一般写上没坏处。
 
@Entity("collectionName")
指定对应的collection的名称,这种情况POJO里必须有无参构造方法
 
@Entity(noClassnameStored = true)
注解不要存储类名。
默认是存储类名的,因为可以把不同的类(特别是相互继承的类)存入同一个collection,例如:
@Entity("animals") abstract class Animal { String name; }
@Entity("animals") Cat extends Animal { ... }
@Entity("animals") Dog extends Animal { ... }
 
@Entity(cap = @CappedAt(...)) 指定为Capped
-------------------------
@Indexes 索引注解可以标记在类上,这样就可以建立多列索引
 
@Entity
@Indexes( @Index("user, -date") )
public class ChangeLog{
Date date;
String user;
Record changedRecord;
}
注:"date"前面的负号表示日期降序排列,便于查找最近的用户
 
还可以添加多个多列索引:
@Indexes({
   @Index("user, -cs"),
   @Index("changedRecord, -cs")})
注:也可以(但不建议)在这里定义单列索引,但最好还是注解在具体的列上
 
-------------------------------------------------
字段注解:
-------------------------
@Id 标记字段为数据库主键
POJO里的对应字段可以是任意支持持久化的类型,例如int,uuid,Object
使用ObjectId可以实现自动生成,否则在每次存储前需要指定此值
-------------------------
@Indexed  注解索引
每次调用datastore.ensureIndexes()时,这些索引就会被应用。
注:如果在一个已经存在数据和索引的系统上,调用datastore.ensureIndexes()方法,将不会产生任何操作,所以可以放心的调用。
 
@Indexed(value=IndexDirection.ASC, name="upc", unique=true, dropDups=true)
value:索引方向,默认是IndexDirection.ASC,还可以是IndexDirection.DESC和IndexDirection.BOTH
name:索引名,默认是mongoDB自动生成
unique:是否唯一索引,默认为flase
dropDups:通知唯一索引静默删除已有的重复元素,只保留第一个,默认为false
-------------------------
@Embedded 注解需要嵌入的对象(形成一个对象树来读取/写入)
被嵌入的对象将嵌套在父对象里,被存入同一个collection中,所以被嵌入的对象里不允许出现@Id注解。
也可以指定被嵌入对象在mongoDB里的属性名:
@Embedded("blog_comments")
-------------------------
@Property POJO属性注解
在POJO中,java原生类型和基本类型默认是不需要注解即可完成读取和写入的(除非注解了@Transient)。
mongoDB只支持四种数据类型:Integer,Long,Double,String
morphia会自动映射java基本数据类型和String,这些类型的数组,以及List,Set,Map到mongoDB中。
另外,以下对象也会被自动转换和读取/写入:
enum 转为String存储
java.util.Date 转为毫秒数存储
java.util.Locale 转为String存储
com.mongodb.DBRef
com.mongodb.ObjectId
 
注:morphia默认使用POJO属性名作为collection里的字段名,这个行为可以被覆盖:
@Property("my_integer")
private int myInt;
-------------------------
@Reference 引用注解,标记某一个字段存在另一个collection中。
该注解有三种属性:
lazy:懒汉模式,被调用时才从数据库获取此字段
ignoreMissing:读取引用失败时不产生异常
concreteClass:产生的实例的类类型
例子:
@Entity public class BlogEntry { @Id private ObjectId id; @Reference private Author author;}
@Entity public class Author { @Id private ObjectId id;}
注:被引用的对象必须存在于mongoDB中,然后才能存储引用其的对象。
注:morphia默认使用POJO里引用的属性名作为collection里的字段名,这个行为可以被覆盖:
@Reference("blog_authors")
private List<Author> authors;
-------------------------
集合类相关的属性注解:
morphia支持Collection(List,Set,Map):
private Set<String> tags;
private Map<String,Translation> translations;
@Reference private List<Article> relatedArticles;
 
默认情况下,morphia读取数据创建实例时会使用以下实现类:
java.util.ArrayList
java.util.HashSet
java.util.HashMap
 
这个行为可以被覆盖:
@Property(concreteClass = java.util.TreeSet.class)
private Set<String> tags;
 
@Embedded(concreteClass = java.util.TreeMap.class)
private Map<String,Translation> translations;
 
@Reference(concreteClass = java.util.Vector.class)
private List<Article> relatedArticles;
-------------------------
@Transient 标记字段被忽略,包括读取/写入
@Serialized 字段被转为二进制后储存
@NotSaved 字段可以被读取,但在写入时忽略
@AlsoLoad 字段可以被任何支持的名字所读取
@Version 版本号标记,自动实现乐观锁,标记后修改操作时可能会抛出ConcurrentModificationException
 
-------------------------------------------------
Morphia的扩展:
-------------------------
校验扩展ValidationExtension:
符合JSR303,可以直接调用Hibernate validation。
对JSR303 API的轻量级的包装:new ValidationExtension(morphia);
import org.hibernate.validator.constraints.Email;
@Entity
public class Userlike {
        @Id ObjectId id;
        @Email String email;
}
-------------------------
日志重定向扩展:SLF4JExtension
将morphia的运行日志重定向到SLF4J中,引入morphia-logging-slf4j-0.99.jar,在系统启动时执行一次:
MorphiaLoggerFactory.registerLogger(SLF4JLoggerImplFactory.class);
 
-------------------------------------------------
手动映射对象到DBObjects(以便传递给底层的driver):
-------------------------
创建Morphia实例(建议只创建一次,然后重用它):
Morphia morphia = new Morphia();
morphia.map(BlogEntry.class).map(Author.class);
每一个这样手动映射的类都会被检查,如果失败会抛出MappingException。
 
也可以让morphia自动扫描某个包,并自动映射:
morphia.mapPackage("my.package.with.only.mongo.entities");
-------------------------
高级使用:从morphia里直接调用底层的java driver存储数据(手动):
Morphia morphia = ...;
Mongo mongo = ...;
DB db = mongo.getDB("BlogSite");
 
//这是注解过的POJO
BlogEntry blogEntry = ...;
 
//让morphia将POJO转为java driver需要的DBObject
DBObject blogEntryDbObj = morphia.toDBObject(blogEntry);
 
//用java driver将其写入mongodb
db.getCollection("BlogEntries").save(blogEntryDbObj);
-------------------------
高级使用:从morphia里直接调用底层的java driver读取数据(手动):
Morphia morphia = ...;
Mongo mongo = ...;
DB db = mongo.getDB("BlogSite");
 
//要读取的ID
String blogEntryId = ...;
 
//调用java driver从mongdoDB取出一个DBObject
BasicDBObject blogEntryDbObj = (BasicDBObject) db.getCollection("BlogEntries").findOne(new BasicDBObject("_id", new ObjectId(blogEntryId));
 
//让morphia将DBObject转为POJO
BlogEntry blogEntry = morphia.fromDBObject(BlogEntry.class, blogEntryDbObj);
 
-------------------------------------------------
DAO层的封装:
morphia已经提供了一个DAO接口和一个BasicDAO类。
我们只要继承BasicDAO类,覆盖其中的构造方法,将mongo和morphia对象传入即可:
public class BlogEntryDAO extends BasicDAO<BlogEntry, ObjectId> {
    public BlogEntryDAO( Morphia morphia, Mongo mongo,String dbName) {
        super(mongo, morphia, dbName);
    }
}
然后就可以实现大部分操作:
BlogEntryDAO blogEntryDAO = new BlogEntryDAO(...);
 
ObjectId  blogEntryId = ...;
BlogEntry myBlogEntry = blogEntryDAO.get(blogEntryId);//查询
 
myBlogEntry.setTitle("My Blog Entry");
blogEntryDAO.save(myBlogEntry);//保存
 
blogEntryDAO.deleteById(myBlogEntry.getId());//删除
 
然后还需要自定义一些查询方法(注意这里用了正则匹配文章标题):
public List<BlogEntry> findByTitle( String title ) {
    Pattern regExp = Pattern.compile(title + ".*", Pattern.CASE_INSENSITIVE);
    return ds.find(entityClazz).filter("title", regExp).sort("title").asList();
}
 
星游注:话说,加入DAO层不就是为了与morphia解耦么?现在DAO层的父类本身就在morphia包里,“这不科学呀。。。”
建议参照其BasicDAO,自己写一个,这才实现了与持久层解耦:
http://code.google.com/p/morphia/source/browse/trunk/morphia/src/main/java/com/google/code/morphia/dao/BasicDAO.java
分享到:
评论

相关推荐

    使用Morphia框架操作mongodb

    本文将深入探讨如何使用Morphia框架来操作MongoDB。 首先,你需要在项目中添加Morphia和MongoDB驱动的依赖。如果你使用的是Maven,可以在pom.xml文件中加入以下依赖: ```xml &lt;groupId&gt;org.mongodb.morphia ...

    MongoDB 入门教程笔记

    MongoDB 入门教程笔记

    MongoDb ORM 框架(构建类似 sql 的体验,体验风格与 wood 类似)

    MongoDB ORM(对象关系映射)框架是一种工具,它允许开发者使用类似SQL的方式来操作MongoDB数据库,从而在非关系型数据库中实现更加直观和高效的数据管理。MongoDB因其灵活性、可扩展性和高性能,在现代Web应用中被...

    SpringMVC+Shiro+MongoDB基础框架

    这个"SpringMVC+Shiro+MongoDB基础框架"项目提供了一个空白的起点,开发者可以在这个基础上快速搭建具备用户认证、权限控制和NoSQL数据库功能的Web应用。通过进一步的定制和扩展,可以满足各种复杂场景的需求。

    MongoDB3.2实战笔记

    ### MongoDB 3.2 实战笔记 #### 一、前言 MongoDB 是一款非常流行的开源文档型数据库系统,以其高性能、高可用性以及灵活的数据模型而受到广泛欢迎。本篇实战笔记主要针对 MongoDB 3.2 版本,涵盖其安装、配置、...

    基于MyBatisPlus模式的MongoDB操作框架设计源码

    该项目是基于MyBatisPlus模式的MongoDB操作框架设计源码,总计315个文件,其中包括299个Java源文件、5个XML配置文件、2个Git忽略文件、2个Markdown文档、2个PNG图片文件、1个LICENSE文件以及多个配置文件(包括...

    MongoDB学习笔记

    自己在学习MongoDB的一些笔记,里面有各个查询选择器的使用截图,还有一些索引的介绍。

    使用 Morphia 和 MongoDB 实现域模型持久性(ZZ)

    Morphia 是一个 Java 框架,它为 MongoDB 提供了对象数据映射(Object Data Mapping,简称 ODM)功能,使得开发者可以更方便地在 Java 对象与 MongoDB 文档之间进行转换。本篇文章将详细介绍如何使用 Morphia 和 ...

    《mongodb入门》读书笔记

    ### MongoDB入门知识点详解 #### MongoDB概述 MongoDB是一款开源、高性能、无模式的文档型数据库系统,被广泛应用于Web应用及大数据处理等场景。它采用了BSON(Binary JSON)格式来存储数据,使得数据存储更加灵活...

    mongoDb源码和笔记

    这份"mongoDb源码和笔记"的资源包含两部分:源码和笔记。源码是MongoDB的原始代码,通过阅读源码,开发者可以学习到数据库系统的架构设计、数据存储、查询优化、并发控制以及网络通信等方面的知识。笔记可能是作者在...

    mongodb+springmvc+morphia

    后续提交放在https://github.com/zdsiyan/watermelon 上, 用eclipse导入该工程需安装m2eclipse,jetty等查件. 另外.settings下的org.eclipse.wst.common.component文件如下: ...&lt;/project-modules&gt;

    使用Morphia和MongoDB实现域模型持久性

    MongoDB是面向文档的开源数据库,Morphia是面向MongoDB 的类型安全的对象映射库。本文解释了在文档和对象之间进行映射的好处,并演示了如何使用Morphia来实现这个功能。然后演示了如何持久保存、加载、删除和查询...

    mongodb入门教程笔记

    MongoDB的查询操作包括基本的`find`、`findOne`、`count`等,还可以使用聚合框架(Pipeline)进行复杂的数据处理。例如,`db.collection.aggregate([pipeline])`可以实现数据过滤、分组、排序等多种操作。此外,通过...

    MongoDB学习总结笔记

    6. **高级特性**:"MongoDB的高级特性及数据库引用.docx"可能涉及复制集(用于高可用性)、分片(用于水平扩展)、事务处理、聚合框架等复杂功能。 7. **MongoDB与PHP的集成**:"MongoDB与PHP的简单应用.docx"讲解...

    MongoDB基础教程笔记 dept.json

    MongoDB练习文件

    MongoDB基础教程笔记 emp.json

    MongoDB练习文件

    php7.1版本安装mongodb扩展踩坑笔记

    ### PHP 7.1 版本安装 MongoDB 扩展踩坑笔记 #### 背景与问题概述 本文档旨在解决在 Homestead 环境下为 PHP 7.1 版本安装 MongoDB 扩展时遇到的问题。具体表现为:通过 `pecl install mongodb` 命令安装后,在 `...

    MongoDB数据库学习笔记

    MongoDB 是一种流行的开源NoSQL数据库,以其灵活性和高性能而受到广泛应用。它的名称来源于“Humongous”,意指处理大量数据的能力。MongoDB 不支持SQL语言,而是采用JSON-like的文档存储格式,这使得它在处理非结构...

    Morphia 操作 MongoDB.docx

    Morphia是针对MongoDB的一个Java持久层框架,它提供了简单易用的API,使得开发者能够方便地在Java应用程序中操作MongoDB数据库。 【Morphia的安装与配置】 要在Java项目中使用Morphia,首先需要下载MongoDB的Java...

Global site tag (gtag.js) - Google Analytics