290 likes | 301 Views
Hibernate OGM is an ORM framework implementing JPA that supports multiple products like Infinispan, Ehcache, Redis, MongoDB, CouchDB, Neo4j, and Cassandra.
E N D
Mug Jan 2016
MUG Hibernate OGM Markus Dorner markus.dorner@ebcont.com EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Features Hibernate Object/Grid Mapper • JPA support • Interesting for projects already using an ORM framework implementing JPA • Supports multiple products • Key/Value: Infinispan; Ehcache; Redis (tech preview) • Document: MongoDB (Configuration) ; CouchDB (tech preview) • Graph: Neo4j • Wide-column: Cassandra (tech preview) EBCONT enterprisetechnologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Overview Basics • Same mechanics as ORM frameworks implementing JPA • Reuse of modules e.g. JPQL processing engine • NoSQL drivers to interact with the underlying data-store • Map your data with JPA annotations • Persistence context • First level cache • Same pitfalls • flush, clear • Object dirty handling • JPQL query language support EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Agenda Topics fortoday • Bootstrapping in Wildfly • Adding Hibernate OGM Module • persistence.xml • Mapping • CRUD • Basic operations • Hibernate Session object • JPQL • Native queries • Transactions EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Bootstrapping Wildfly Configuration and dependencies • Add Hibernate OGM Module to Wildfly • Download the Module jar for your server version • hibernate-ogm-modules-wildfly9.4.2.0.Final • extract it to the wildfly modules directory • <wildfly home dir>\modules\system\layers\base • Maven Setup • Bom already provided for easier version management • Dependency in your pom to provided <dependency> <groupId>org.hibernate.ogm</groupId> <artifactId>hibernate-ogm-bom</artifactId> <version>4.2.0.Final</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.hibernate.ogm</groupId> <artifactId>hibernate-ogm-mongodb</artifactId> <scope>provided</scope> </dependency> EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Classloading Defining deployment structures • Define deployment structure in your project war • \src\main\webapp\WEB-INF\jboss-deployment-structure.xml <jboss-deployment-structure> <deployment> <dependencies> <module name="org.hibernate" slot="ogm" services="export" /> <module name="org.hibernate.ogm.mongodb" slot="main" services="export" /> </dependencies> </deployment> </jboss-deployment-structure> EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Configuring persistence unit Persistence.xml <persistence-unit name="mongodb" transaction-type="JTA"> <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider> <jta-data-source>java:/DefaultDS</jta-data-source> <class>com.ebcont.model.LogEntry</class> <properties> <property name="hibernate.transaction.jta.platform“ value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" /> <property name="jboss.as.jpa.providerModule" value="org.hibernate:ogm" /> <property name="hibernate.ogm.datastore.provider" value="mongodb" /> <property name="hibernate.ogm.datastore.database" value="ogm-eval" /> <property name="hibernate.ogm.datastore.host" value="localhost" /> <property name="hibernate.ogm.datastore.port" value="27017" /> <property name="hibernate.ogm.datastore.create_database“ value="true" /> </properties> </persistence-unit> EBCONT enterprisetechnologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Mapping Annotations Overview • Mapping over XML possibleaswell. Not covered in thisslides. • @Table • Refers to the collection the entity is stored • @Id, @Column • @Embeddable • @ElementCollection • Associations @ManyToOne, @OneToOne, … EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Collection level @Table @Entity @Table(name = "logs") public class LogEntry { • Control the name of the collection • Omitted @Table leads to LogEntryas collection name • Default naming is the class name • Hibernate OGM provides no DDL generation • Non-existing collections will be created • Possible to define custom naming strategies EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Primary key Overview • @Id • Simplest case map field to _id • { "_id" : "ba3db765-c2b8-4cb0-b91c-afe7cd414873“ } • ObjectId encouraged. Can be enforced with @Type if you want to work with Strings. • @EmbeddedId • "_id" : { "role" : “admin", "user" : "max" } • @IdClass • Generated json equal to @EmbeddedId • @Id over multiple fields • No dot notation needed when referring to ID fields in JPQL @Id private String id; @EmbeddedId private UserRoleId id; @Entity @IdClass(UserRoleId.class) public class UserRole { EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Embedded List @ElementCollection @ElementCollection private List<String> subsystems; • Stores the List as Array into the document • List is managed and changes flushed into the database • In the ORM world this Annotation was not very performant • Change on List caused deletion of all rows and a reinsert of all rows -> low performance in relational world • In MongoDB it is encouraged to embed lists for performance • Mind the 16MB document limit "subsystems" : [ "DEBIT", "PAYMENT" ] EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Example @Entity @Table(name = "logs") @NamedQueries({ @NamedQuery(name=LogEntry.FIND_OTHERS, query="SELECT l FROM LogEntry l WHERE l.id <> :id") }) publicclassLogEntry { publicstaticfinal String FIND_OTHERS = "LogEntry.findOthers"; @Id private String id = java.util.UUID.randomUUID().toString(); @Temporal(TemporalType.TIMESTAMP) @Column(nullable = false) private Calendar logTimestamp = Calendar.getInstance(); @Embedded privateSystemDatasystemData; @ElementCollection(fetch = FetchType.EAGER) private List<String> subsystems; @Version privatelongversion; EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Example - Json output > db.logs.findOne() { "_id" : "ba3db765-c2b8-4cb0-b91c-afe7cd414873", "logTimestamp" : "2016/01/15 15:23:13:367 +0100", "systemData" : { "eventType" : "READ", "systemId" : "SERVICEBUS-NODE1" }, "version" : NumberLong(0), "subsystems" : [ "DEBIT", "PAYMENT" ] } EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Associations Overview • Configurable with property in persistenxe.xml • hibernate.ogm.datastore.document.association_storage • IN_ENTITY (default): store association information within the entity • ASSOCIATION_DOCUMENT: store association information in a dedicated document per association • 2 ways supported to store this information • Configurable also on entity level annotation based • Not covered by JPA Annotations • e.g. @AssocationStorage • Programmatic configuration available too EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Unidirectional one-to-one Strategy IN_ENTITY • Car Entity has no reference to its child relation • Wheel has a reference to its parent • @JoinColumn( name = “car" ) to control the field name • Reference: to-one-associations @Entity publicclass Car { @Id private String id; { "_id" :"V_01", "brand" : "Mercedes" } @Entity publicclass Wheel { @Id privateString id; @OneToOne privateCar car; { "_id" : "W001", "car_id" : "V_01" } EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Bidirectional one-to-one Strategy IN_ENTITY @Entity publicclass Car { @Id private String id; @OneToOne private Wheel wheel; • Car Entity has reference to its child relation • Wheel has a reference to its parent • { "_id" : "V_01", • "brand" : "Mercedes", • "wheel_id" : "W001" } @Entity publicclass Wheel { @Id privateString id; @OneToOne privateCar car; { "_id" : "W001", "car_id" : "V_01" } EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Unidirectional many-to-one Strategy IN_ENTITY • Json result similar to @OneToOne • Reference: to-many-associations @Entity publicclassMember { @Id private String id; private String name; @ManyToOne private JavaUserGroup memberOf; { "_id" : "jerome", • "name" : "Jerome", "memberOf_id" : "summer_camp" } { "_id" : “james", "name" : “James“ "memberOf_id" : "summer_camp" } EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Bidirectional many-to-one Strategy IN_ENTITY • @OneToMany keeps references in Array • @ManyToOne side @Entity publicclassSalesForce{ @Id private String id; private String corporation; @OneToMany(mappedBy = "salesForce") private Set<SalesGuy> salesGuys; { "_id" : "red_hat", "corporation" : "Red Hat", "salesGuys" : [ "eric", "simon" ] } { "_id" : "simon", "name" :"Simon", "salesForce_id" : "red_hat" } EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Bidirectional many-to-many Strategy IN_ENTITY @Entity publicclassAccountOwner { @Id private String id; @ManyToMany private Set<BankAccount> bankAccounts; • Both sides keep references in array • Account Owner • Bank Account { "_id" : "owner_1", "bankAccounts" : [ "account_1" ] } @Entity publicclassBankAccount { @Id private String id; private String accountNumber; @ManyToMany( mappedBy = "bankAccounts" ) private Set<AccountOwner> owners; { "_id" : "account_1", "accountNumber" : "X2345000“, "owners" : [ "owner_1", "owner2222" ] } EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Query Log Deeper look at generated Queries • save:ogm-eval.logs { "_id" : "eventId" , "logTimestamp" : "2016/01/21 11:56:04:686 +0100" , "systemData" : { "eventType" : "READ" , "systemId" : "SERVICEBUS-NODE1"} , "version" : 0} • save:ogm-eval.LogSubEvent { "_id" : "subEventId" , "logEntry_id" : "eventId" , "eventName" : "Log Event1"} • update:ogm-eval.logs { "_id" : "eventId"} { "$set" : { "subEvents" : [ "subEventId"]}} • update:ogm-eval.LogSubEvent { "_id" : "otherSubEventId"} { "$set" : { "logEntry_id" : "eventId" , "eventName" : "Log Event2"}} • update:ogm-eval.logs { "_id" : "eventId"} { "$set" : { "subEvents" : [ "subEventId" , "otherSubEventId"]}} • update:ogm-eval.logs { "_id" : "eventId"} { "$set" : { "subsystems" : [ "PAYMENT" , "DEBIT"]}} • Looks superfluous. Why not written on initial insert? (@ElementCollection) • Each @OneToMany Element triggers one update. • Reason: no real tx over collections EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
CRUD Insert • Default transaction attribute REQUIRED (default) • Starts transaction if none started • More to transactions later • Entity added to persistence context • Flush or commit inserts the document to the collection • Flush in ORM flushes the statements to the database, but the changes are only visible after commit • JPQL query triggers flush @Stateless @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) publicclassLogService { @PersistenceContext(name = "mongodb") privateEntityManager em; publicLogEntry log() { LogEntry entry = new LogEntry(); initSystemData(entry); initSubsystems(entry); em.persist(entry); returnentry; } } EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
CRUD Basic Entitymanageroperations • EntityManager#find(…) • Multiple calls of find do not trigger a find operation to MongoDB • Changes on the entity will be detected and updates flushed back to the database • Use em.clear() or em.detach(Object) to remove objects from the persistence context. • Need direct access to the Hibernate Session? • (org.hibernate.ogm.hibernatecore.impl.OgmSessionImpl) em.getDelegate() • EntityManager#remove(…) EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
JPQL Supported Language Elements • comparisons "<", "<=", "=", ">=“, ">“, “<>” • IS NULL and IS NOT NULL • booleanoperators AND, OR, NOT • LIKE, IN and BETWEEN • ORDER BY • inner JOIN on embedded collections • projections of regular and embedded properties • Not supported: • JP-QL functions in particular aggregation functions like count • JP-QL update and delete queries • Check documentation for more details on your provider. EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
JPQL Examples Supported Elements • projection of the entity property • select id, description from Hypothesis h where id = 16 • projection of the embedded properties • select h.address.streetfrom Hypothesis h where h.id = 16 • IN Statement • select h from Hypothesis h where h.position IN (2, 3, 4) • Range with named parameters • select h from Hypothesis h where h.orderBETWEEN :start and :end • More examples: JP-QL Functions EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Native Queries Examples and Limitations • 2 ways of native queries supported • find queries specifying the search criteria only • queries specified using the MongoDB CLI syntax • db.collection.find( … • Experimental • Parameters not supported. Query#setParameter() will not work. // criteria-only find syntax String query1 = "{ $and: [ { name : 'Portia' }, { author : 'Oscar Wilde' } ] }"; Poem poem = (Poem) em.createNativeQuery( query1, Poem.class ).getSingleResult(); // projection via CLI-syntax String query4 = "db.WILDE_POEM.count({ 'name' : 'Athanasia' })"; Object[] count = (Object[]) em.createNativeQuery( query4 ).getSingleResult(); EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Transaction Management Overview • MongoDB transactions • spanning multiple documents with $isolated • lock on collection, single thread mode during write • Does not work on sharded clusters • Spanning multiple collections not supported • Hibernate OGM provides transactions by flushing the persistence context as late as possible • Partial changes possible if error occurs during flush • No compensation logic out-of-the-box, but API to implement one • Transaction Attributes supported within the above limitations • See @TransactionAttribute on Class or Method level @Stateless @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) publicclassLogService { @PersistenceContext(name = "mongodb") privateEntityManager em; publicLogEntry log() { EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Advantages The good • Container managed transactions • Easily combined with other persistence units • Declarative transaction configuration • Easier introduction to projects already using JPA • Team already knows the framework design principles • Step-by-step migration • Moving entities one by one from the relational database to MongoDB • service code may only need small adaptions • example audit log entity • Evaluate best NoSQL fit for your application • Switch products by switching the provider (hibernate.ogm.datastore.provider) • Integration with Hibernate Search EBCONT enterprise technologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna
Disadvantages The bad • The same pitfalls from ORM apply to OGM • Keep an eye on the persistence context • Detach unnecessary objects to free memory • n+1 problem • Mapping in a relational database differs from a schema-less database like MongoDB • e.g. Joins not effective • Use associations (@OneToMany, …) with care • fetch joins. Not recommended for large lists. • Dirty detection may be expensive with large embedded lists • No Parameters in native queries • Better use driver directly • No criteria API support yet EBCONT enterprisetechnologies, Millennium Tower, Handelskai 94–96, A–1200 Vienna