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.