get
根据@id返回一个实例,find()的简洁使用,查找不到结果时,返回null
Hotel hotel = ds.get(Hotel.class, hotelId);
Datastore ds = ...
//use in a loop
for(Hotel hotel : ds.find(Hotel.class, "stars >", 3))
print(hotel);
//get back as a list
List hotels = ds.find(Hotel.class, "stars >", 3).asList();
//sort the results
List 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();
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();
Datastore ds = ...
ds.delete(Hotel.class, "Grand Sierra Resort");
//use a query
ds.delete(ds.createQuery(Hotel.class).filter("pendingDelete", true));
Hotel grandSierra = ds.findAndDelete(ds.get(Hotel.class, "Grand Sierra Resort"));
public void loggedIn()
{
long now = System.currentTimeMillis();
Query me = datastore.createQuery(User.class).field(Mapper.ID_KEY).equal(id);
UpdateOperations ops = datastore.createUpdateOperations(User.class).set("lastLogin", now);
ds.update(me , ops);
lastLogin = now;
}
m.map(MyEntity.class);
ds.ensureIndexes(); //creates all defined with @Indexed
ds.ensureCaps(); //creates all collections for @Entity(cap=@CappedAt(...))
Updating(On the server)总之,当你通过Datesote调用update方法时就会向MongoDB服务器发送一个修改已存在数据的指令。
- interface Datastore {
- ...
- /** updates all entities found with the operations*/
UpdateResults update(Query query, UpdateOperations ops); - /** updates all entities found with the operations; if nothing is found insert the update as an entity if "createIfMissing" is true*/
UpdateResults update(Query query, UpdateOperations ops, boolean createIfMissing); - /** updates the first entity found with the operations*/
UpdateResults updateFirst(Query query, UpdateOperations ops); - /** updates the first entity found with the operations; if nothing is found insert the update as an entity if "createIfMissing" is true*/
UpdateResults updateFirst(Query query, UpdateOperations ops, boolean createIfMissing); - /** updates the first entity found with the operations; if nothing is found insert the update as an entity if "createIfMissing" is true*/
UpdateResults updateFirst(Query query, T entity, boolean createIfMissing); - }
- public interface UpdateOperations
{ - /** sets the field value */
- UpdateOperations
set(String fieldExpr, Object value); - /** removes the field */
- UpdateOperations
unset(String fieldExpr); - /** adds the value to an array field*/
- UpdateOperations
add(String fieldExpr, Object value); - UpdateOperations
add(String fieldExpr, Object value, boolean addDups); - /** adds the values to an array field*/
- UpdateOperations
addAll(String fieldExpr, List> values, boolean addDups); - /** removes the first value from the array*/
- UpdateOperations
removeFirst(String fieldExpr); - /** removes the last value from the array*/
- UpdateOperations
removeLast(String fieldExpr); - /** removes the value from the array field*/
- UpdateOperations
removeAll(String fieldExpr, Object value); - /** removes the values from the array field*/
- UpdateOperations
removeAll(String fieldExpr, List> values); - /** decrements the numeric field by 1*/
- UpdateOperations
dec(String fieldExpr); - /** increments the numeric field by 1*/
- UpdateOperations
inc(String fieldExpr); - /** increments the numeric field by value (negatives are allowed)*/
- UpdateOperations
inc(String fieldExpr, Number value); - }
The Field Expression属性表达式是用在所有的操作上的,可以是单个的属性名,也可以是用点“.”连接的嵌套属性。在表达式中你也可以使用位置操作副($)。在属性表达式中没有标准,你可以使用任何在MongoDB服务端有效符号。事例初始化一下所有的实例创建的连接和Morphia实例都使用的一下代码。
- Morphia morphia = new Morphia();
- morphia.map(Hotel.class).map(Address.class);
- Datastore datastore = morphia.createDatastore("MorphiaSampleDb");
- Hotel hotel = new Hotel("Fairmont", 3, new Address("1 Rideau Street", "Ottawa", "K1N8S7", "Canada"));
- datastore.save(hotel);
- UpdateOperations
ops; - // This query will be used in the samples to restrict the update operations to only the hotel we just created.
- // If this was not supplied, by default the update() operates on all documents in the collection.
- // We could use any field here but _id will be unique and mongodb by default puts an index on the _id field so this should be fast!
- Query
updateQuery = datastore.createQuery(Hotel.class).field("_id").equal(hotel.getId()); - // The Mapper class also provides a public static of the default _id field name for us...
- Query
updateQuery = datastore.createQuery(Hotel.class).field(Mapper.ID_KEY).equal(hotel.getId()); 注意: 使用的是 equal() 而不是 equals()。
- @Entity("hotels")
- public class Hotel
- {
- @Id
- private ObjectId id;
- private String name;
- private int stars;
- @Embedded
- private Address address;
- @Embedded
- List
roomNumbers = new ArrayList (); - // ... getters and setters
- }
- @Embedded
- public class Address
- {
- private String street;
- private String city;
- private String postalCode;
- private String country;
- // ... getters and setters
- }
set/unset
- // 改变Hotel的name属性值
- ops = datastore.createUpdateOperations(Hotel.class).set("name", "Fairmont Chateau Laurier");
- datastore.update(updateQuery, ops);
- //也可以操作嵌套文档, 改变address的city属性值
- ops = datastore.createUpdateOperations(Hotel.class).set("address.city", "Ottawa");
- datastore.update(updateQuery, ops);
- // 删除Hotel的name属性值
- // 当下会访问Hotel时name属性为null
- ops = datastore.createUpdateOperations(Hotel.class).unset("name");
- datastore.update(updateQuery, ops);
inc/dec
- // 'stars'属性增长一
- ops = datastore.createUpdateOperations(Hotel.class).inc("stars");
- datastore.update(updateQuery, ops);
- // 'stars'属性值增长4
- ops = datastore.createUpdateOperations(Hotel.class).inc("stars", 4);
- datastore.update(updateQuery, ops);
- // 'stars'属性值减少1
- ops = datastore.createUpdateOperations(Hotel.class).dec("stars"); // 和 .inc("stars", -1) 相同
- datastore.update(updateQuery, ops);
- // 'stars'属性值减少4
- ops = datastore.createUpdateOperations(Hotel.class).inc("stars", -4);
- datastore.update(updateQuery, ops);
add/All当在一个不是数组的属性上进行数组操作时MongoDB将会抛出错误。
- // 把一个值放入到数组中 array() (+v 0.95)
- // same as .add("roomNumbers", 11, false)
- ops = datastore.createUpdateOperations(Hotel.class).add("roomNumbers", 11);
- datastore.update(updateQuery, ops); // [ 11 ]
removeFirst/Last/All
- ops = datastore.createUpdateOperations(Hotel.class).set("roomNumbers", 11);
- datastore.update(updateQuery, ops);
- // 由于没有rooNumbers数组将会引起错误
- ops = datastore.createUpdateOperations(Hotel.class).add("roomNumbers", 11, false);
- datastore.update(updateQuery, ops); // causes error
- // 删除roomNummbers属性
- ops = datastore.createUpdateOperations(Hotel.class).unset("roomNumbers");
- datastore.update(updateQuery, ops);
- // use the 3rd parameter to add duplicates
- // add to end of array, same as add()
- ops = datastore.createUpdateOperations(Hotel.class).add("roomNumbers", 11, false);
- datastore.update(updateQuery, ops); // [ 11 ]
- // no change since its a duplicate... doesn't cause error
- ops = datastore.createUpdateOperations(Hotel.class).add("roomNumbers", 11, false);
- datastore.update(updateQuery, ops); // [ 11 ]
- // push onto the end of the array
- ops = datastore.createUpdateOperations(Hotel.class).add("roomNumbers", 12, false);
- datastore.update(updateQuery, ops); // [ 11, 12 ]
- // add even if its a duplicate
- ops = datastore.createUpdateOperations(Hotel.class).add("roomNumbers", 11, true);
- datastore.update(updateQuery, ops); // [ 11, 12, 11 ]
- //given roomNumbers = [ 1, 2, 3 ]
- ops = datastore.createUpdateOperations(Hotel.class).removeFirst("roomNumbers");
- datastore.update(updateQuery, ops); // [ 2, 3 ]
- //given roomNumbers = [ 1, 2, 3 ]
- ops = datastore.createUpdateOperations(Hotel.class).removeLast("roomNumbers");
- datastore.update(updateQuery, ops); // [ 1, 2 ]
- ops = datastore.createUpdateOperations(Hotel.class).removeLast("roomNumbers");
- datastore.update(updateQuery, ops); // [ 1 ]
- ops = datastore.createUpdateOperations(Hotel.class).removeLast("roomNumbers");
- datastore.update(updateQuery, ops); // [] empty array
- //given roomNumbers = [ 1, 2, 3, 3 ]
- ops = datastore.createUpdateOperations(Hotel.class).removeAll("roomNumbers", 3);
- datastore.update(updateQuery, ops); // [ 1, 2 ]
- //given roomNumbers = [ 1, 2, 3, 3 ]
- ops = datastore.createUpdateOperations(Hotel.class).removeAll("roomNumbers", Arrays.asList(2, 3));
- datastore.update(updateQuery, ops); // [ 1 ]
Multiple Operations
你也可以在一个update指令中执行多个updae操作。updateFirst方法 在默认的驱动和shell上这是默认的行为。在Morphia中我们认为修改所有的符合条件的结果是最好的默认选择(如下)。
- //设置城市名称为Ottawa和是stars的增长1
- ops = datastore.createUpdateOperations(Hotel.class).set("city", "Ottawa").inc("stars");
- datastore.update(updateQuery, ops);
- //如果你在同一个属性上执行多次同样的指令操作,结果将会变化,即:只有最后一次有效
- ops = datastore.createUpdateOperations(Hotel.class).inc("stars", 50).inc("stars"); //stars只增长1
- ops = datastore.createUpdateOperations(Hotel.class).inc("stars").inc("stars", 50); //stars只增长50
- //你不能在同一个属性上执行相矛盾的操作。
- ops = datastore.createUpdateOperations(Hotel.class).set("stars", 1).inc("stars", 50); //引起错误
{name: "Fairmont", stars: 5}, {name: "Last Chance", stars: 3}
- ops = datastore.createUpdateOperations(Hotel.class).inc("stars", 50);
- // (+v 0.95 now takes into account the order())
- // morphia 执行updateFirst方法仅仅执行第一个符合查询条件的数据项
- datastore.updateFirst(datastore.find(Hotel.class).order("stars"), ops); //仅仅修改Last Chance
- datastore.updateFirst(datastore.find(Hotel.class).order("-stars"), ops); // 仅仅修改 Fairmont
update 方法
- //default shell version is to match first
- //shell version has a multi to indicate to update all matches, not just first
- //to mimic morphia operation, set multi = false
- db.collection.update( criteria, objNew, upsert, multi );
- ops = datastore.createUpdateOperations(Hotel.class).inc("stars", 50);
- // morphia 默认的update是修改所有的Hotels
- datastore.update(datastore.createQuery(Hotel.class), ops); //所有的hotel都会增长
createIfMissing (overload parameter)
- //equivalent morphia shell version is... upsert = false, multi = true
- db.collection.update( criteria, objNew, false, true );
所有的update都被重载支持一个"createIfMissing"参数。
- ops = datastore.createUpdateOperations(Hotel.class).inc("stars", 50);
- //修改, 如果没有找到就添加一个。
- datastore.updateFirst(datastore.createQuery(Hotel.class).field("stars").greaterThan(100), ops, true);
- // creates { "_id" : ObjectId("4c60629d2f1200000000161d"), "stars" : 50 }
- //equivalent morphia shell version is... upsert = true
- db.collection.update( criteria, objNew, true, multi );
DAO
针对Mongo数据库访问,morphia提供了访问的基本的接口便于开发人员实现。
DAO接口类:
public interface DAO
DAO底层实现类:使用DAO如下:package com.google.code.morphia.dao; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.List; import com.google.code.morphia.Datastore; import com.google.code.morphia.DatastoreImpl; import com.google.code.morphia.Key; import com.google.code.morphia.Morphia; import com.google.code.morphia.query.Query; import com.google.code.morphia.query.QueryResults; import com.google.code.morphia.query.UpdateOperations; import com.google.code.morphia.query.UpdateResults; import com.mongodb.DBCollection; import com.mongodb.Mongo; import com.mongodb.WriteConcern; import com.mongodb.WriteResult; /** * @author Olafur Gauti Gudmundsson * @author Scott Hernandez */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class BasicDAO
implements DAO { protected Class entityClazz; protected DatastoreImpl ds; public BasicDAO(Class entityClass, Mongo mongo, Morphia morphia, String dbName) { initDS(mongo, morphia, dbName); initType(entityClass); } public BasicDAO(Class entityClass, Datastore ds) { this.ds = (DatastoreImpl) ds; initType(entityClass); } /** * Only calls this from your derived class when you explicitly declare the generic types with concrete classes
** {@code class MyDao extends DAO
* */ protected BasicDAO(Mongo mongo, Morphia morphia, String dbName) { initDS(mongo, morphia, dbName); initType(((Class} * ) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0])); } protected BasicDAO(Datastore ds) { this.ds = (DatastoreImpl) ds; initType(((Class ) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0])); } protected void initType(Class type) { this.entityClazz = type; ds.getMapper().addMappedClass(type); } protected void initDS(Mongo mon, Morphia mor, String db) { ds = new DatastoreImpl(mor, mon, db); } /** * Converts from a List to their id values * * @param keys * @return */ protected List> keysToIds(List > keys) { ArrayList ids = new ArrayList(keys.size() * 2); for (Key key : keys) ids.add(key.getId()); return ids; } /** The underlying collection for this DAO */ public DBCollection getCollection() { return ds.getCollection(entityClazz); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#createQuery() */ public Query createQuery() { return ds.createQuery(entityClazz); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#createUpdateOperations() */ public UpdateOperations createUpdateOperations() { return ds.createUpdateOperations(entityClazz); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#getEntityClass() */ public Class getEntityClass() { return entityClazz; } /* (non-Javadoc) * @see com.google.code.morphia.DAO#save(T) */ public Key save(T entity) { return ds.save(entity); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#save(T, com.mongodb.WriteConcern) */ public Key save(T entity, WriteConcern wc) { return ds.save(entity, wc); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#updateFirst(com.google.code.morphia.query.Query, com.google.code.morphia.query.UpdateOperations) */ public UpdateResults updateFirst(Query q, UpdateOperations ops) { return ds.updateFirst(q, ops); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#update(com.google.code.morphia.query.Query, com.google.code.morphia.query.UpdateOperations) */ public UpdateResults update(Query q, UpdateOperations ops) { return ds.update(q, ops); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#delete(T) */ public WriteResult delete(T entity) { return ds.delete(entity); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#delete(T, com.mongodb.WriteConcern) */ public WriteResult delete(T entity, WriteConcern wc) { return ds.delete(entity, wc); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#deleteById(K) */ public WriteResult deleteById(K id) { return ds.delete(entityClazz, id); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#deleteByQuery(com.google.code.morphia.query.Query) */ public WriteResult deleteByQuery(Query q) { return ds.delete(q); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#get(K) */ public T get(K id) { return ds.get(entityClazz, id); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#findIds(java.lang.String, java.lang.Object) */ public List findIds(String key, Object value) { return (List ) keysToIds(ds.find(entityClazz, key, value).asKeyList()); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#findIds() */ public List findIds() { return (List ) keysToIds(ds.find(entityClazz).asKeyList()); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#findIds(com.google.code.morphia.query.Query) */ public List findIds(Query q) { return (List ) keysToIds(q.asKeyList()); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#exists(java.lang.String, java.lang.Object) */ public boolean exists(String key, Object value) { return exists(ds.find(entityClazz, key, value)); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#exists(com.google.code.morphia.query.Query) */ public boolean exists(Query q) { return ds.getCount(q) > 0; } /* (non-Javadoc) * @see com.google.code.morphia.DAO#count() */ public long count() { return ds.getCount(entityClazz); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#count(java.lang.String, java.lang.Object) */ public long count(String key, Object value) { return count(ds.find(entityClazz, key, value)); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#count(com.google.code.morphia.query.Query) */ public long count(Query q) { return ds.getCount(q); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#findOne(java.lang.String, java.lang.Object) */ public T findOne(String key, Object value) { return ds.find(entityClazz, key, value).get(); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#findOne(com.google.code.morphia.query.Query) */ public T findOne(Query q) { return q.get(); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#find() */ public QueryResults find() { return createQuery(); } /* (non-Javadoc) * @see com.google.code.morphia.DAO#find(com.google.code.morphia.query.Query) */ public QueryResults find(Query q) { return q; } /* (non-Javadoc) * @see com.google.code.morphia.DAO#getDatastore() */ public Datastore getDatastore() { return ds; } public void ensureIndexes() { ds.ensureIndexes(entityClazz); } } package com.easyway.mongodb.morphia; import java.util.List; import com.easyway.mongodb.morphia.basic.Hotel; import com.google.code.morphia.Morphia; import com.google.code.morphia.dao.BasicDAO; import com.google.code.morphia.query.UpdateOperations; import com.mongodb.Mongo; /** * 数据访问层类的使用 */ public class HotelDAO extends BasicDAO
{ public HotelDAO(Morphia morphia, Mongo mongo, String dbName) { super(mongo, morphia, dbName); } /** * 统计四星级以上酒店数量 * @return */ public long countHotel(){ return count(createQuery().field("stars").greaterThanOrEq(4)); } /** * 查询酒店 * @return */ public List queryHotelPhone(){ return createQuery().field("phoneNumbers").sizeEq(1).asList(); } /** * 查询酒店 * @param hotel */ public List queryHotel(Hotel hotel){ return find(createQuery().filter("stars", 4).order("address.address_street")).asList(); } /** * 修改酒店信息 * @param hotel */ public void batchUpdateHotel(){ UpdateOperations mods = createUpdateOperations().inc("stars", 1); update(createQuery().filter("stars", 4), mods); } }
Query
该接口能够过滤查询条件、排序和位移,设置查询结果数量。实现了QueryResults 接口。
FilterQuery q = ds.createQuery(MyEntity.class).filter("foo >", 12);
所有条件默认为“and”Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).filter("foo <", 30);
Operators
filter操作与mongo操作对照
operator | mongo op |
= | $eq |
!=, <> | $ne |
>, <, >=,<= | $gt, $lt, $gte, $lte |
in | $in |
nin | $nin |
elem | $elemMatch |
exists | $exists |
all | $all |
size | $size |
... | ... |
Query q = ds.createQuery(MyEntity.class).field("foo").equal(1);
q.field("bar").greaterThan(12);
q.field("bar").lessThan(40);
方法对照method | operation | comment |
exists | $exists | |
doesNotExist | $exists | |
greaterThan, greaterThanOrEq, lessThan, lessThanOrEq | $gt, $gte, $lt, $lte | |
equal, notEqual | $eq, $ne | |
hasThisOne | $eq | |
hasAllOf | $all | |
hasAnyOf | $in | |
hasNoneOf | $nin | |
hasThisElement | $elemMatch | |
sizeEq | $size |
Geo-spatial
所有的空间查询方法都在"near和within"。near查询根据距离提供查询结果,最近的排第一个。方法通过常量指定一个新的范围参数
method | operation | comment |
near(x,y) | $near | |
near(x,y,r) | $near | (w/maxDistance of r) |
within(x,y,r) | $within + $center | |
within(x1,y1,x2,y2) | $within + $box |
@Entity
static private class Place {
@Id protected ObjectId id;
protected String name = "";
@Indexed(IndexDirection.GEO2D)
protected double[] loc = null;
public Place(String name, double[] loc) {
this.name = name;
this.loc = loc; }
private Place() {}
}
Place place1 = new Place("place1", new double[] {1,1});
ds.save(place1);
Place found = ds.find(Place.class).field("loc").near(0, 0).get();
Query q = ad.createQuery(Person.class);
q.or(
q.criteria("firstName").equal("scott"),
q.criteria("lastName").equal("scott")
);
属性名也可以像在本地MongoDB查询中那样使用"."标识
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");
Datastore ds = ...
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);
你可以通过一个或多个属性名对结果进行升序或降序排序
Datastore ds = ...
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");
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).limit(100);
你可以通过在查询是指定一个偏移值使服务器跳过一些记录元素。这将比使用几个属性的范围进行查询要低效的多。如下所示:
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12).offset(1000);
MongoDB也支持只返回一些属性。这在应用中显得有点奇怪,但是这个在修剪掉一部分嵌套时是非常有用的方法。返回实体对象部分字段。
Datastore ds = ...
MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(true, "foo").get();
val = e.getFoo(); // only field returned
...
MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(false, "foo").get();
val = e.getFoo(); // only field not returned
字段名参数可以是string的list或者string数组MyEntity e = ds.createQuery(MyEntity.class).retrievedFields(true, "foo", "bar").get();
val = e.getFoo(); // fields returned
vak = e.getBar(); // fields returned
method | does |
get() | 返回第一个Entity -- using limit(1) |
asList() | 以List返回所有结果 -- 如果数据量很大,将付出很大的代价 |
fetch() | 返回一个 Iterable 对象 |
asKeyList() | 以List的方式返回所有数据项的Key |
fetchEmptyEntities() | 和 fetch() 一样,但是仅检索返回id列 |
Datastore ds = ...
Query q = ds.createQuery(MyEntity.class).filter("foo >", 12);
//single entity
MyEntity e = q.get();
e = q.sort("foo").get();
//for
for (MyEntity e : q)
print(e);
//list
List entities = q.asList();