Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » EclipseLink and Dynamic Entities
EclipseLink and Dynamic Entities [message #514151] Fri, 12 February 2010 16:02 Go to next message
Klaus G ønbæk is currently offline Klaus G ønbækFriend
Messages: 3
Registered: January 2010
Junior Member
Hi All

I have spent a couple of weeks working with dynamic entities , either created on the fly from custom models, or created from existing tables, and I would like to share some of my findings.

Here is an example which tries to find an existing dynamic entity, if it doesn't exist, it will be created, and then it will be updated.

package com.example

import org.eclipse.persistence.descriptors.*;
import org.eclipse.persistence.descriptors.changetracking.*;
import org.eclipse.persistence.dynamic.*;
import org.eclipse.persistence.exceptions.*;
import org.eclipse.persistence.jpa.*;
import org.eclipse.persistence.jpa.dynamic.*;
import org.eclipse.persistence.sessions.*;

import java.util.*;
import javax.persistence.*;

/**
 * Simple Example that creates a dynamic type, and uses it with find(), and persist()
 * @author Klaus Groenbaek
 */
public class DynamicEntityExample {

    public static void main(String[] args) {

        EntityManagerFactory emf = getEMF();
        EntityManager em = emf.createEntityManager();

        try {
            // you need a session to get the dynamic classloader
            Session session = JpaHelper.getEntityManager(em).getServerSession();
            DynamicClassLoader dcl = DynamicClassLoader.lookup(session);

            // The name of the database table, and the JavaClass backing your dynamic entity
            String tableName = "MyTable";
            String objectName = tableName;
            // create the dynamic class
            Class<?> clazz = dcl.createDynamicClass("com.example." + objectName);

            DynamicTypeBuilder builder = null;
            // DynamicTypeBuilder, causes em.Find() to throw NPE
            //builder = new DynamicTypeBuilder(clazz, null, tableName);

            // JPADynamicTypeBuilder, if you use this builder entities wont update after they are changed
            //builder = new JPADynamicTypeBuilder(clazz, null, tableName);

            // Use a CustomTypeBuilder to fix these shortcummings
            builder = new CustomDynamicTypeBuilder(clazz, null, tableName);
            // set mapping for two properties
            builder.addDirectMapping("MyID", Long.class, "ID");
            builder.addDirectMapping("MyString", String.class, "STRING");
            builder.setPrimaryKeyFields("ID");
            // get an register the type
            DynamicType type = builder.getType();
            DynamicHelper dynamicHelper = new JPADynamicHelper(em);
            dynamicHelper.addTypes(true /* create table */, false /*foreign key constraints*/, type);


            // try to find the entity with ID 42. Will fail if you used DynamicTypeBuilder.
            Long pk = 42L;
            DynamicEntity entiry = (DynamicEntity) em.find(type.getJavaClass(), pk);
            if (entiry == null) {
                // now create a new entity and save it
                entiry = type.newDynamicEntity();
                entiry.set("MyID", pk);
                entiry.set("MyString", "Hello World");
                em.getTransaction().begin();
                em.persist(entiry);
                em.getTransaction().commit();
            }

            // now change a property and store the object again this generates an UPDATE (If you used the CustomDynamicTypeBuilder)
            entiry.set("MyString", "Godbye World");
            em.getTransaction().begin();
            em.persist(entiry);
            em.getTransaction().commit();
        }
        catch (DynamicException e) {
            e.printStackTrace();
        }
        finally {
            em.close();
        }



    }

    /**
     * Creates the EntityManagerFactory, connecting to a MySQL DB
     * @return Returns an entityManagerFactory
     */
    private static EntityManagerFactory getEMF() {

        HashMap<String, String> connectionProperties;
        connectionProperties = new HashMap<String, String>();
        connectionProperties.put("eclipselink.jdbc.password", "pass");
        connectionProperties.put("eclipselink.jdbc.user", "user");
        connectionProperties.put("eclipselink.jdbc.driver", "com.mysql.jdbc.Driver");
        connectionProperties.put("eclipselink.jdbc.url", "jdbc:mysql://localhost/db");
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("ObjectDatabase", connectionProperties);
        return emf;


    }

    /**
     * The default DymanicTypeBuilder has two problems 1. It doesn't define a CMPPolicy (which result in a NPE when
     * using em.find() [EntityManagerImpl.findInternal(), 2.0.0.jar line 644], The JPADynamicTypeBuilder addresses this
     * issue <br>
     * 2. The default ObjectChangePolicy on Dynamic Entities is AttributeChangeTrackingPolicy, however this causes
     * the entities to not update when persist os called on a modified object
     */
    static class CustomDynamicTypeBuilder extends DynamicTypeBuilder {

        public CustomDynamicTypeBuilder(Class<?> dynamicClass, DynamicType parentType, String... tableNames) {
            super(dynamicClass, parentType, tableNames);
        }

        @Override
        protected void configure(ClassDescriptor descriptor, String... tableNames) {
            super.configure(descriptor, tableNames);

            if (descriptor.getCMPPolicy() == null) {
                descriptor.setCMPPolicy(new DynamicIdentityPolicy());
            }
            // override the default policy, which is AttributeChangeTrackingPolicy
            descriptor.setObjectChangePolicy(new DeferredChangeDetectionPolicy());
        }
    }
}


The documentation on building dynamic types using EclipseLink 2.0.0 is outdated, so I'm not sure I have done everything they way you're supposed to - Comments are welcome.
I'm hoping this example will save others the vast amounts of time I have spent digging around in the EclipseLink source code. Especially that entities created using JPADynamicTypeBuilder won't update when they are re-persisted.

Other observations:
Dynamic Entities doesn't work with JPA 2.0 CriteriaQuery Sad
Thanks to the EclipseLink team for a great library, that saves a lot of time when you have to support 8 different databses.

[Updated on: Fri, 12 February 2010 16:03]

Report message to a moderator

Re: EclipseLink and Dynamic Entities [message #516145 is a reply to message #514151] Mon, 22 February 2010 20:56 Go to previous messageGo to next message
Mike Norman is currently offline Mike NormanFriend
Messages: 35
Registered: July 2009
Member
Klaus: thank you very much for your posting - its always gratifying to see someone use software you wrote 'in the real-world'!

In your posting, you bring up 3 issues:
i) a NPE regarding the CMPPolicy when using JPADynamicTypeBuilder
This should be working now (2.0.1) - what version of EclipseLink are you using?

ii) detecting changes upon a second persist
I'm going to have to get some advice from the JPA experts around here - my reading of the spec indicates that the second 'em.persist()' call should throw an exception since the object already exists.

iii) Criteria queries - as with ii), need to ask for some help

I'll get back to you as soon as possible with what I find out.

Thanks again!
(Mit freundlichen Grüße)
--
Mike Norman | Principal Software Designer | 613.288.4638
Oracle Server Technologies | EclipseLink Product
45 O'Connor Street, Suite 400 | Ottawa, ON K1P 1A4 | (fax) 613.238.2818
Re: EclipseLink and Dynamic Entities [message #516346 is a reply to message #514151] Tue, 23 February 2010 16:11 Go to previous messageGo to next message
Doug Clarke is currently offline Doug ClarkeFriend
Messages: 155
Registered: July 2009
Senior Member
Klaus,

I believe we have a bug with dynamic entities not having their attribute-change-tracker properly setup after the first entity transaction commit call. After this point the entity passed to persist should be managed with a change-tracker attached to capture future changes. Mike and I will review and log the appropriate bug(s).

Please note however that your second call to EntityManager.persist in the second entity transaction should have no effect. If you pass an already existing entity to this method on the same EntityManager where it was originally persisted EclipseLink will ignore the call.

I believe the problem is related to this line having no effect:

entiry.set("MyString", "Godbye World");

Doug

Re: EclipseLink and Dynamic Entities [message #516378 is a reply to message #516145] Tue, 23 February 2010 19:19 Go to previous messageGo to next message
Mike Norman is currently offline Mike NormanFriend
Messages: 35
Registered: July 2009
Member
I've opened a bug on this matter: https://bugs.eclipse.org/bugs/show_bug.cgi?id=303651 - Attribute change tracking not working for Dynamic Entities
Re: EclipseLink and Dynamic Entities [message #517296 is a reply to message #516378] Fri, 26 February 2010 20:25 Go to previous message
Mike Norman is currently offline Mike NormanFriend
Messages: 35
Registered: July 2009
Member
Klaus: can you try the '20100226' nightly build (http://www.eclipse.org/eclipselink/downloads/nightly.php)
and report back if it solves your issue?

Thanks in advance,
Mike
Previous Topic:OneToMany does not populate Foreign Key or null foreign key
Next Topic:eclipselink problem with commons-dbcp
Goto Forum:
  


Current Time: Thu Nov 27 12:04:11 GMT 2014

Powered by FUDForum. Page generated in 0.15664 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software