对象 & 文档映射

优质
小牛编辑
127浏览
2023-12-01

在对象(Object,也称实体Entity)、文档(Document)之间实现自动转换,是bugu-mongo的最核心功能,这能让你直接用面向对象的概念来操作MongoDB数据库,而不用去关心底层的数据库细节。

在这方面,bugu-mongo提供了:

注解:

  • @Entity
  • @Id
  • @Property
  • @Embed
  • @EmbedList
  • @Ref
  • @RefList
  • @Ignore
  • @CustomCodec

接口:

  • BuguEntity

抽象类:

  • SimpleEntity

BuguEntity接口

要使得某个Java Entity能和MongoDB Document实现相互转换,该Entity需要实现BuguEntity接口,该接口中有2个方法,如下:

public void setId(String id);

public String getId();

注解

先看一段示例代码:

import com.bugull.mongo.BuguEntity;
import com.bugull.mongo.annotations.Entity;
import com.bugull.mongo.annotations.Id;
import com.bugull.mongo.annotations.IdType;
import com.bugull.mongo.annotations.Property;
import com.bugull.mongo.annotations.Embed;
import com.bugull.mongo.annotations.EmbedList;
import com.bugull.mongo.annotations.Ref;
import com.bugull.mongo.annotations.RefList;
import java.util.List;

@Entity
public class Foo implements BuguEntity{
    @Id
    private String id;
    private String name;
    @Property
    private int level;
    @Embed
    private EmbedFoo embed;
    @EmbedList
    private List<EmbedFoo> embedList;
    @Ref
    private FatherFoo father;
    @RefList
    private List<ChildFoo> children;
    @Ignore
    private double sumScore;

    @Override
    public String getId() {
        return id;
    }

    @Override
    public void setId(String id) {
        this.id = id;
    }

    ...getter and setter...

}

public class EmbedFoo {
    private float x;
    private int y;
    ...getter and setter...
}

@Entity(name="father")
public class FatherFoo implements BuguEntity{
    @Id(type=IdType.AUTO_INCREASE, start=1000)
    private String id;
    private Date date;
    @Ref
    private FatherFoo father;
    ...getter and setter...
}

@Entity(name="child")
public class ChildFoo implements BuguEntity{
    @Id
    private String id;
    private List<String> list;
    ...getter and setter...
}

各个注解的含义如下:

@Entity

表示需要映射到MongoDB中的一个实体。该注解有4个属性:

  • name——String型,表示其在MongoDB中的collection的名称。name属性可以省略,默认使用类名的全小写。
  • capped——boolean型,表示该Entity类对应的是capped collection,缺省值为false。
  • capSize——long型,设置capped collection的空间大小,以字节为单位,默认值为-1,表示未设置。
  • capMax——long型,设置capped collection的最多能存储多少个document,默认值为-1,表示未设置。

如果设置了capped=true,则需要设置capSize和capMax两者中的其中一个。

提示:使用@Entity注解的类,必须实现BuguEntity接口。

@Id

映射到MongoDB中的_id。@Id属性在Java代码中必须为String类型,但其在MongoDB中的数据类型,则根据对@Id的type属性的设置而定。

@Id的type属性,有以下3个枚举值:

  • IdType.AUTO_GENERATE——默认值。表示由MongoDB自动产生id值。在MongoDB中,id被保存成为一个ObjectId对象,形式如:"_id" : ObjectId("4eb8beeaeaad0a390144f084")。
  • IdType.AUTO_INCREASE——表示id值自动增长。如1、2、3、4...。在MongoDB中,id被保存成Long类型的数值。
  • IdType.USER_DEFINE——表示id值由程序员自己定义。比如,String id = UUID.randomUUID().toString();在MongoDB中,id被保存成一个字符串。

如果使用type=IdType.AUTO_INCREASE,那么还可以设置start参数,使得id从指定的值开始增长。

一个例子如下:

@Id(type=IdType.AUTO_INCREASE, start=1000)
private String id;

注意: 使用自动增长的时候,bugu-mongo框架需要锁定整个collection,然后从中查询出最大的id值,如果对写入速度的要求比较高,请谨慎使用。

@Property

该注解可以省略。它用来映射基本数据类型,包括:

  • String
  • int/Integer
  • long/Long
  • short/Short
  • byte/Byte
  • float/Float
  • double/Double
  • boolean/Boolean
  • char
  • Date
  • BigDecimal
  • 上述基本数据类型组成的数组、List、Set、Queue、Collection、Map等。

@Property有2个属性:

  • name——String型,用于指定映射到MongoDB collection中某个field。属性name可以省略,表示采用与Entity的Field相同的名称。
  • lazy——Boolean型,含义是:取出该实体的列表时,是否取出该field值。如果为true,表示不取出。默认值为false。

对lazy属性的详细讲解,请查看lazy和cascade

@Ignore

表示该属性不需要被映射。当保存实体时,该属性不会保存至MongoDB;同样,该属性也不会从MongoDB中取出。

@Embed

表示该属性是一个嵌入的对象,包括自定义class和enum类型。嵌入的对象(如EmbedFoo)不需要@Entity注解,不需要实现BuguEntity接口,也不需要有@Id。

跟@Property一样,@Embed也有2个属性:name、lazy,含义也是一样的。

嵌入对象的属性,可以用点号“.”来引用,例如查询:

FooDao dao = ...
List list = dao.query().is("embed.x", 3.14F).results();  //查询出全部embed.x=3.14的Foo对象

@EmbedList

表示该属性是一组嵌入的对象,包括自定义class和enum类型。

@EmbedList注解支持数组、List、Set、Queue、Collection、Map等,但都必须使用泛型。当使用Map的时候,嵌入对象只能作为Map的value,而不能作为key。

注意: 和Embed一样,嵌入的对象都必须是自己定义的Java对象(或者枚举类型)。如果是原始数据类型组成的数组、List、Set、Queue、Collection、Map等,请使用@Property,而不是@EmbedList。

跟@Property一样,@EmbedList也有2个属性:name、lazy,含义也是一样的。

@Ref

表示对另一个对象的引用,在MongoDB中保存的是形如{"father" : DBRef("father", ObjectId("56f499725cad6e05e437fa16"))}这样的一个DBRef。

@Ref有4个属性:

  • name——含义与@Property的参数name一样。
  • impl——如果@Ref标注在一个接口(Interface)上,那么可以用impl指定其实现类。例:
    @Ref(impl=MyFather.class)
    private IFather father;
    
  • cascade——String型,用于指定关联操作。其值可以是C、R、U、D四个字符中的任意一个或多个字符组成。如:cascade="CRUD", cascade="CD", cascade="CR"等等。

    C、R、U、D四个字符分别代表的含义如下:

    • C:Create,创建
    • R:Read,读取
    • U:Update,修改
    • D:Delete,删除

      对cascade属性的详细讲解,请查看lazy和cascade

  • reduced——boolean型,用于指定是否使用精简存储。默认值为false。

    reduced=false时,@Ref字段在MongoDB中保存的是如下这样的一个DBRef:

    { "father" : DBRef("father", ObjectId("56f499725cad6e05e437fa16")) }
    

    reduced=true时,@Ref字段在MongoDB中保存的仅仅是引用对象的id值,没有collection名称,如:

    { "father" : ObjectId(4dcb4d1d3febc6503e4e5933) }
    

    设置reduced=true,可以节省数据库的存储空间,但数据内容不再那么直观。

注意,如果需要用@Ref属性和@Id属性进行表的连接查询,则一定要设置reduced=true

@RefList

表示对另一个对象的引用的集合。

@RefList注解支持数组、List、Set、Queue、Collection、Map等,但都必须使用泛型。当使用Map的时候,引用对象只能作为Map的value,而不能作为key。

@RefList有5个属性:

  • name——含义与@Property的属性name一样。
  • impl——含义与@Ref的属性impl类似,指定集合中元素的实现类。
  • cascade——含义与@Ref的属性cascade一样。
  • reduced——含义与@Ref的属性reduced一样。
  • sort——String型,用于关联取出该List或Set属性时使用的排序规则,其形式是:"{level:1}",或"{level:1, timestamp:-1}"这样的JSON字符串。如果不排序,则不用设置sort值。关于排序的详细说明,请参考这里

自定义编码、解码器

上述注解,几乎能满足所有的应用场景。如果在某些特殊情况下,上述注解仍无法满足应用程序的需要,那么,可以使用@CustomCodec注解,自己定义编码、解码规则。

例如:

@CustomCodec(encoder=MyEncoder.class, decoder=MyDecoder.class)
private MyObject my_object;

encoder代表自定义的编码器,用于将Java对象的值,转换成DBObject(从而保存到数据库)。它需要继承自AbstractEncoder类,实现其中的抽象方法。

decoder代表自定义的解码器,用于从DBObject中取出相应的值(来自于数据库),设置到Java对象中去。它需要继承自AbstractDecoder类,实现其中的抽象方法。

encoder类、decoder类的具体实现方式,可以参考JUnit单元测试中的示例代码,以及框架源码中其它Encoder、Decoder类的代码。

注意:再次强调,@CustomCodec属于高级用法,除非其它注解真的无法满足你的要求,否则没必要使用它。

辅助类:SimpleEntity

bugu-mongo框架提供了BuguEntity接口的一个简单实现:SimpleEntity。你可以不用自己直接实现BuguEntity接口,而是继承自SimpleEntity。

SimpleEntity是一个抽象类,它有如下特点:

  • 有一个id属性,使用默认的IdType.AUTO_GENERATE作为id的类型;
  • 实现了getId()、setId()、equals()、hashCode()、toString()等方法;
  • 实现了Serializable接口;
  • 提供了getTimestamp()方法,能告诉你该document是何时插入进MongoDB的(其原理是从ObjectId中获取创建时间)。

使用SimpleEntity,可以使得你的实体模型的代码非常简洁,如下:

@Entity
public class MyFoo extends SimpleEntity {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}