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/