首页 > 代码库 > Mongodb ODM: morphia

Mongodb ODM: morphia

Mongodb Java Driver

虽然Mongodb提供了java driver,但是如果我们直接使用driver进行mongodb的操作,代码冗余很多,使用不是方便,容易出错。这就像我们在RDBMS中使用sql直接操作数据库一样,大多数时候我们不提倡这样做,更多的时候我们使用MyBatis或者Hibernate做ORM。Mongodb中有这样的工具帮助我们完成ODM吗?

有很多,这里我们就介绍使用morphia作为ODM,因为它看起来比较清爽。https://github.com/mongodb/morphia


Learning Test

我们使用Learning Test的方式来演示我们如何使用Morphia,测试的方式可以参考《Java中的Mongodb单元测试》

为了方便,我创建了一个MorphiaBaseTest

public class MorphiaBaseTest extends MongodbBaseTest {
    protected Datastore datastore;

    @Before
    @Override
    public void setUp() throws Exception {
        super.setUp();
        datastore = new Morphia().createDatastore(mongo, db.getName());
    }
}

MongodbBaseTest见《Java中的Mongodb单元测试》。


最简单的ODM

我们首先完成一个User,这个User只有一个field是name,同时含有一个id作为唯一标示。

UserTest:

public class UserTest extends MorphiaBaseTest {
    @Test
    public void should_get_the_created_user() {
        final User user = new User("kiwi");
        datastore.save(user);

        final User userFromDb = datastore.get(User.class, user.getId());

        assertThat(userFromDb.getName(), is("kiwi"));
        assertThat(userFromDb.getId(), notNullValue());
    }
}


为了让Learning Test通过,对应的User

@Entity("users")
public class User {
    @Id
    private ObjectId id;
    private String name;

    //used for morphia
    User() {
    }

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public ObjectId getId() {
        return id;
    }
}

稍微解释一下,@Entity标示User的实例将会以document的方式存在db中对应的“users”这个collection中。@Id表示的是User中的id做为mongodb中user这个document的id。Morphia会自动去扫描它能发现的每一个field。所以name也会被存到数据库里。


Embedded Document

创建一个新的类:Contact

public class Contact {
    private String location;
    private String phoneNumber;

    //for morphia
    Contact() {
    }

    public Contact(String location, String phoneNumber) {
        this.location = location;
        this.phoneNumber = phoneNumber;
    }

    public String getLocation() {
        return location;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }
}


我们把Contact放在我们的User中:

@Entity("users")
public class User {
    @Id
    private ObjectId id;
    private String name;
    private Contact contact;

    User() {
    }

    public User(String name, Contact contact) {
        this.name = name;
        this.contact = contact;
    }

    //... ...

    public Contact getContact() {
        return contact;
    }
}


编写测试UserTest

    @Test
    public void should_get_user_contact() {
        final User user = new User("kiwi", new Contact("chengdu", "028-83003008"));

        datastore.save(user);

        final User userFromDb = datastore.get(User.class, user.getId());
        assertThat(userFromDb.getContact().getLocation(), is("chengdu"));
        assertThat(userFromDb.getContact().getPhoneNumber(), is("028-83003008"));
    }

通过,通过log你可以发现

insert embedded-mongo.users query: { _id: ObjectId(‘53c5f2c3ca50168dfc4d3362‘), className: "org.kiwi.morphia.User", name: "kiwi",contact: { location: "chengdu", phoneNumber: "028-83003008" }}

在存储的过程中,contact已经被当做user的embedded document了。这是因为在morphia中,其对于对象的默认存储方式就是embedded document。你可以在contact上标明@Embedded,如下所示,效果都是一样的:

    @Embedded
    private Contact contact;


Reference

除了embedded外,还有还有一类比较重要的关系是reference。

创建Car:

@Entity("cars")
public class Car {
    @Id
    private ObjectId id;
    private String carNumber;
    private String brand;
    private int price;

    //for morphia
    Car() {
    }

    public Car(String carNumber, int price, String brand) {
        this.brand = brand;
        this.price = price;
        this.carNumber = carNumber;
    }

    public ObjectId getId() {
        return id;
    }

    public String getCarNumber() {
        return carNumber;
    }

    public String getBrand() {
        return brand;
    }

    public int getPrice() {
        return price;
    }
}

在User上增加对Car的引用(reference):

@Entity("users")
public class User {
    //... ...
    @Reference
    private Car car;

    //... ...

    public void addCar(Car car) {
        this.car = car;
    }

    public Car getCar() {
        return car;
    }
}

UserTest:

    @Test
    public void should_get_user_car() {
        final Car car = new Car("SC0404", 10000, "BMW");
        datastore.save(car);


        final User user = new User("kiwi", new Contact("chengdu", "028-83003008"));
        datastore.save(user);


        user.addCar(car);
        datastore.save(user);


        final User userFromDb = datastore.get(User.class, user.getId());
        assertThat(userFromDb.getCar().getCarNumber(), is("SC0404"));
        assertThat(userFromDb.getCar().getBrand(), is("BMW"));
        assertThat(userFromDb.getCar().getPrice(), is(10000));
    }

测试通过,标明Car在User中是一个reference对象的方法是@Reference。

此时存在数据库的User就是这样,car的类型是DBRef

{ "_id" : ObjectId("53c5fb68ca503473a3fabdc8"), "className" : "org.kiwi.morphia.User", "name" : "kiwi", "contact" : { "location" : "chengdu", "phoneNumber" : "028-83003008" },"car" : DBRef("cars", ObjectId("53c5fb68ca503473a3fabdc7")) }


参考资料

http://www.obsidianscheduler.com/blog/using-mongodb-with-morphia/

http://architects.dzone.com/articles/using-morphia-map-java-objects

http://www.ibm.com/developerworks/java/library/j-morphia/