Статьи

Использование Morphia для отображения объектов Java в MongoDB

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.