MongoDB — это ориентированная на документы система баз данных NoSQL с открытым исходным кодом, которая хранит данные в виде JSON-подобных документов с динамическими схемами. Поскольку он не хранит данные в таблицах, как это делается в обычной настройке реляционной базы данных, он не очень хорошо отображает способ хранения данных в JPA. Morphia — это легковесная библиотека безопасных типов с открытым исходным кодом, предназначенная для преодоления разрыва между драйвером MongoDB Java и объектами домена. Это может быть альтернативой SpringData, если вы не используете Spring Framework для взаимодействия с MongoDB.
В этом посте будут рассмотрены основы сохранения и запроса сущностей по линиям JPA с использованием Morphia и экземпляра базы данных MongoDB.
Этот пример будет использовать четыре POJO. Сначала у нас есть BaseEntity, который является абстрактным классом, содержащим поля Id и Version:
package com.city81.mongodb.morphia.entity;
import org.bson.types.ObjectId;
import com.google.code.morphia.annotations.Id;
import com.google.code.morphia.annotations.Property;
import com.google.code.morphia.annotations.Version;
public abstract class BaseEntity {
@Id
@Property("id")
protected ObjectId id;
@Version
@Property("version")
private Long version;
public BaseEntity() {
super();
}
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
В то время как JPA будет использовать @Column для переименования атрибута, Morphia использует @Property. Другое отличие состоит в том, что @Property должен быть в переменной, тогда как @Column может быть в переменной или в методе get.
Основным объектом, который мы хотим сохранить, является класс Customer:
package com.city81.mongodb.morphia.entity;
import java.util.List;
import com.google.code.morphia.annotations.Embedded;
import com.google.code.morphia.annotations.Entity;
@Entity
public class Customer extends BaseEntity {
private String name;
private List<Account> accounts;
@Embedded
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Как и в JPA, POJO помечается @Entity. Класс также показывает пример @Embedded:
класс Address также помечается @Embedded, как показано ниже:
package com.city81.mongodb.morphia.entity;
import com.google.code.morphia.annotations.Embedded;
@Embedded
public class Address {
private String number;
private String street;
private String town;
private String postcode;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getTown() {
return town;
}
public void setTown(String town) {
this.town = town;
}
public String getPostcode() {
return postcode;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
}
Наконец, у нас есть класс Account, класс клиента которого имеет коллекцию:
package com.city81.mongodb.morphia.entity;
import com.google.code.morphia.annotations.Entity;
@Entity
public class Account extends BaseEntity {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Выше показано только небольшое подмножество того, какие аннотации могут применяться к классам домена. Дополнительную информацию можно найти по адресу http://code.google.com/p/morphia/wiki/AllAnnotations.
Класс Example, показанный ниже, выполняет шаги, связанные с подключением к экземпляру MongoDB, заполнением сущностей, сохранением их и последующим извлечением:
package com.city81.mongodb.morphia;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import com.city81.mongodb.morphia.entity.Account;
import com.city81.mongodb.morphia.entity.Address;
import com.city81.mongodb.morphia.entity.Customer;
import com.google.code.morphia.Datastore;
import com.google.code.morphia.Key;
import com.google.code.morphia.Morphia;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
/**
* A MongoDB and Morphia Example
*
*/
public class Example {
public static void main( String[] args ) throws UnknownHostException, MongoException {
String dbName = new String("bank");
Mongo mongo = new Mongo();
Morphia morphia = new Morphia();
Datastore datastore = morphia.createDatastore(mongo, dbName);
morphia.mapPackage("com.city81.mongodb.morphia.entity");
Address address = new Address();
address.setNumber("81");
address.setStreet("Mongo Street");
address.setTown("City");
address.setPostcode("CT81 1DB");
Account account = new Account();
account.setName("Personal Account");
List<Account> accounts = new ArrayList<Account>();
accounts.add(account);
Customer customer = new Customer();
customer.setAddress(address);
customer.setName("Mr Bank Customer");
customer.setAccounts(accounts);
Key<Customer> savedCustomer = datastore.save(customer);
System.out.println(savedCustomer.getId());
}
Выполнение первых нескольких строк приведет к созданию хранилища данных. Этот интерфейс предоставит возможность получать, удалять и сохранять объекты в экземпляре MongoDB «банк».
Вызов метода mapPackage для объекта morphia определяет, какие объекты отображаются этим экземпляром Morphia. В этом случае все, кто в комплекте поставляется. Существуют и другие альтернативы для отображения классов, включая метод map, который принимает один класс (этот метод может быть объединен в цепочку, поскольку возвращаемый объект является объектом morphia), или передача набора классов конструктору Morphia.
После создания экземпляров сущностей их можно сохранить, вызвав save для экземпляра хранилища данных, и их можно найти с помощью первичного ключа через метод get. Вывод из класса Example будет выглядеть примерно так:
11-Jul-2012 13:20:06 com.google.code.morphia.logging.MorphiaLoggerFactory chooseLoggerFactory INFO: LoggerImplFactory set to com.google.code.morphia.logging.jdk.JDKLoggerFactory 4ffd6f7662109325c6eea24f Mr Bank Customer
Есть много других методов в интерфейсе хранилища данных, и их можно найти вместе с другими Javadocs на http://morphia.googlecode.com/svn/site/morphia/apidocs/index.html
. Альтернативой прямому использованию хранилища данных является используйте встроенную поддержку DAO. Это можно сделать, расширив класс BasicDAO, как показано ниже для объекта Customer:
package com.city81.mongodb.morphia.dao;
import com.city81.mongodb.morphia.entity.Customer;
import com.google.code.morphia.Morphia;
import com.google.code.morphia.dao.BasicDAO;
import com.mongodb.Mongo;
public class CustomerDAO extends BasicDAO<Customer, String> {
public CustomerDAO(Morphia morphia, Mongo mongo, String dbName) {
super(mongo, morphia, dbName);
}
}
Чтобы затем использовать это, класс Example можно изменить (и улучшить, чтобы показать запрос и удаление):
...
CustomerDAO customerDAO = new CustomerDAO(morphia, mongo, dbName);
customerDAO.save(customer);
Query<Customer> query = datastore.createQuery(Customer.class);
query.and(
query.criteria("accounts.name").equal("Personal Account"),
query.criteria("address.number").equal("81"),
query.criteria("name").contains("Bank")
);
QueryResults<Customer> retrievedCustomers = customerDAO.find(query);
for (Customer retrievedCustomer : retrievedCustomers) {
System.out.println(retrievedCustomer.getName());
System.out.println(retrievedCustomer.getAddress().getPostcode());
System.out.println(retrievedCustomer.getAccounts().get(0).getName());
customerDAO.delete(retrievedCustomer);
}
...
С выходом от запуска выше показано ниже:
11-Jul-2012 13:30:46 com.google.code.morphia.logging.MorphiaLoggerFactory chooseLoggerFactory INFO: LoggerImplFactory set to com.google.code.morphia.logging.jdk.JDKLoggerFactory Mr Bank Customer CT81 1DB Personal Account
Этот пост охватывает только несколько кратких основ Morphia, но показывает, как он может помочь преодолеть разрыв между JPA и NoSQL.