Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] Unable to implement update for existing source object to point to existing target object

Rohit:
I have put together a simple JUnit4 testsuite that walks thru the lifecycle of building a Dynamic JPA project:

setUp
 - deal with database properties
 - build an EntityManagerFactory: the stuff with MyPersistenceProviderResolver and MyPersistenceProvider
   looks more complicated that is - a Dynamic JPA project doesn't need the persistence.xml and orm.xml files
  This is a way (in JPA2) to override the default PersistenceProvider.

createTypes
 - pretty much the same as how you map the classes

populate
 - (obvious)

setDept
 - sets the 1:1 relationship on an employee to point to the department created in the above populate() method

tearDown
 - drop tables

The assert statements are just checking for the number of SQL statements in each category; if you wish, you could
dig into the tracker's query cache and find the exact strings.

Hope this helps,
---
Oracle
Mike Norman | Principal Software Designer
Phone: +6132884638 | Fax: +6132382818
Server Technologies | EclipseLink Product
Oracle Canada | 45 O'Connor Street, Suite 400, Ottawa, Ontario | K1P 1A4

Hardware and
        Software, Engineered to Work Together
Oracle is
        committed to developing practices and products that help protect
        the environment

On 05/01/2011 1:18 AM, Rohit Banga wrote:
Just to add
I have attached some sample code. In the code I am having a relationship between the employee and phone tables as well but that should not make a difference.

I am using the following documentation:
1. http://wiki.eclipse.org/Using_Basic_Unit_of_Work_API_%28ELUG%29#Associating_an_Existing_Source_to_an_Existing_Target_Object
2. http://wiki.eclipse.org/Using_Advanced_Unit_of_Work_API_%28ELUG%29#How_to_Force_a_Version_Field_Update

The first one talks about the scenario that I am trying to implement. I don't see any significant differences in my implementation. Moreover the second page mentions that
"when a relationship is changed in a one-to-many or one-to-one target foreign key mapping, by default, the version field (if any) of the affected object is not changed"


I am unable to correlate the information contained in the two links. Could you please help me understand it?

Thanks

On 1/5/2011 8:49 AM, Rohit Banga wrote:
Hi Tom,

1. Yes I am invoking commit on UnitOfWork. I have set the log level to FINEST for the UnitOfWork. The SQL does not show up in the logs. The tables are hence not updated. The unit of work is in the end after printing the changes.
If I try to modify any direct to field mapping for the employee then that does show up in the change set. But if eclipselink calculates the changeset piecemeal (does it?), then like you said the change in the the one-to-many mapping may not show up in the changeset.


2. By modification of the list I meant:

List<DynamicEntity> empList = (DynamicEntity) dep.get("empinfo.depinfo");
empList.add(emp);

The introduction of the above code does not work. I am not even sure if the relationship on the other side "must" or "need not" be modified.

3. Yes I have the same mappings. Sorry I did not make it clear in the original question.

Department to Employee - one to many. 
Employee to Department - one to one.
The employee table has a foreign key constraint - depid.
join attribute is "empinfo.depinfo" for both the mappings.

Thanks and Regards
Member Technical Staff 
Oracle Server Technologies
91 80 41085685

----- Original Message -----
From: tom.ware@xxxxxxxxxx
To: rohit.banga@xxxxxxxxxx, eclipselink-users@xxxxxxxxxxx
Sent: Wednesday, January 5, 2011 12:27:21 AM GMT +05:30 Chennai, Kolkata, Mumbai, New Delhi
Subject: Re: [eclipselink-users] Unable to implement update for existing source object to point to existing target object

Hi Rohit,

   Do you actually commit your UnitOfWork?  i.e. are you seeing the problem at 
commit time through the absence of SQL, or are you simply getting the change set 
prior to commit?  If you are getting the change set prior to commit, the changes 
may simply not have been calculated yet.

   What do you mean by: "I have also tried to populate the list corresponding to 
the employees of  department 1010, but even that does not work."

   Please show where your unitOfWork.commit() calls are in the code you have 
included.

   Also, I assume these are the same mappings as you have shown in previous 
postings?


-Tom

Rohit Banga wrote:
  Hi All

I have two tables

1. Employee (id, name, depid)
2. Department (depid, depname)

I have created employee and department dynamic types. I have a OneToMany 
relationship between the department and employee types.

I have to update the department of an employee. I am using the following 
code:

DynamicEntity emp = helper.newDynamicEntity(empBuilder.getType().getName());
emp.set("id", 147);
emp = (DynamicEntity) unitOfWork.readObject(emp);

DynamicEntity dep = helper.newDynamicEntity(depBuilder.getType().getName());
dep.set("depid", 1010);
dep = (DynamicEntity) unitOfWork.readObject(dep);

emp.set("empinfo.depinfo", dep);


However this does not update the depid for the employee row. The change 
is not present in the change set for the unit of work.

The unit of work that I have acquired from the session does not contain 
any objects before the readObject calls in the code above.

*Can you please tell me what is wrong with the above code?*

I have also tried to populate the list corresponding to the employees of 
department 1010, but even that does not work.

*Is it mandatory to update the list or is it sufficient to modify the 
emp object alone?*

When I use forceVersionUpdate for the emp object then, the session log 
does show UpdateQuery in the logs however no update query is executed 
against the database.
*What is the reason for this behavior?*
*
*
*Happy New Year!*
*
*
Thanks
Rohit Banga
Member Technical Staff
Oracle India Private Limited
91 80 41085685


------------------------------------------------------------------------

_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/eclipselink-users

--
Thanks and Regards
Rohit Banga
Member Technical Staff
Oracle Server Technologies
_______________________________________________ eclipselink-users mailing list eclipselink-users@xxxxxxxxxxx https://dev.eclipse.org/mailman/listinfo/eclipselink-users
package test;

//javase imports
import static org.junit.Assert.assertEquals;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//java eXtension imports
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceProviderResolver;
import javax.persistence.spi.PersistenceProviderResolverHolder;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.ProviderUtil;

//JUnit4 imports
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

//EclipseLink imports
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.config.TargetServer;
import org.eclipse.persistence.dynamic.DynamicClassLoader;
import org.eclipse.persistence.dynamic.DynamicEntity;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl;
import org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl;
import org.eclipse.persistence.internal.jpa.deployment.SEPersistenceUnitInfo;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.JpaHelper;
import org.eclipse.persistence.jpa.dynamic.JPADynamicHelper;
import org.eclipse.persistence.jpa.dynamic.JPADynamicTypeBuilder;
import org.eclipse.persistence.logging.DefaultSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.logging.SessionLogEntry;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.FetchGroupTracker;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.SessionEvent;
import org.eclipse.persistence.sessions.SessionEventAdapter;
import org.eclipse.persistence.sessions.server.Server;

public class EmpDeptDynamicJPATestSuite {

    static final String DB_URL_KEY = "db.url";
    static final String DEFAULT_DB_URL = "jdbc:oracle:thin:@localhost:1521:ORCL";
    static final String DB_USER_KEY = "db.user";
    static final String DEFAULT_DB_USER = "scott";
    static final String DB_PWD_KEY = "db.pwd";
    static final String DEFAULT_DB_PWD = "tiger";
    static final String LOGGING_LEVEL_KEY = "eclipselink.logging.level";
    static final String LOGGING_LEVEL = "off";
    //hard-code to Oracle
    static final String DEFAULT_DRIVER = "oracle.jdbc.OracleDriver";
    static final String DEFAULT_PLATFORM = "org.eclipse.persistence.platform.database.oracle.Oracle11Platform";
    static final String PACKAGE_PREFIX = "test";
    static final String PERSISTENCE_UNIT_NAME = PACKAGE_PREFIX;
    static final String EMP_TABLE_NAME = "emp_empdep";
    static final String DEPT_TABLE_NAME = "dep_empdep";
    // test fixtures
    
    static EntityManagerFactory emf = null;
    static JPADynamicHelper dynamicHelper = null;
    static JPADynamicTypeBuilder empBuilder = null;
    static JPADynamicTypeBuilder depBuilder = null;
    static QuerySQLTracker queryTracker = null;
    
    @BeforeClass
    public static void setUp() {
        Map<String, Object> properties = new HashMap<String, Object>();
        // Configure database properties
        properties.put(PersistenceUnitProperties.JDBC_USER,
            System.getProperty(DB_USER_KEY, DEFAULT_DB_USER));
        properties.put(PersistenceUnitProperties.JDBC_PASSWORD,
            System.getProperty(DB_PWD_KEY, DEFAULT_DB_PWD));
        properties.put(PersistenceUnitProperties.JDBC_URL,
            System.getProperty(DB_URL_KEY, DEFAULT_DB_URL));
        properties.put(PersistenceUnitProperties.TARGET_DATABASE, DEFAULT_PLATFORM);
        properties.put(PersistenceUnitProperties.JDBC_DRIVER, DEFAULT_DRIVER);
        // Configure logging.
        properties.put(PersistenceUnitProperties.LOGGING_LEVEL, LOGGING_LEVEL);
        // Dynamic entities are the same as statically pre-woven entities
        properties.put(PersistenceUnitProperties.WEAVING, "STATIC");
        // Running in 'plain' JavaSE - configure 'no-server' platform
        properties.put(PersistenceUnitProperties.TARGET_SERVER, TargetServer.None);
        //
        DynamicClassLoader dcl = new DynamicClassLoader(EmpDeptDynamicJPATestSuite.class.getClassLoader());
        // Use DynamicClassLoader
        properties.put(PersistenceUnitProperties.CLASSLOADER, dcl);
        PersistenceProviderResolverHolder.setPersistenceProviderResolver(
            new MyPersistenceProviderResolver());
        emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME, properties);
        dynamicHelper = new JPADynamicHelper(emf);
        Server serverSession = JpaHelper.getServerSession(emf);
        ConversionManager cm = serverSession.getPlatform().getConversionManager();
        cm.setLoader(dcl);
    }

    @AfterClass
    public static void tearDown() {
        Server serverSession = JpaHelper.getServerSession(emf);
        serverSession.executeNonSelectingSQL("DROP TABLE " + DEPT_TABLE_NAME);
        serverSession.executeNonSelectingSQL("DROP TABLE " + EMP_TABLE_NAME);
    }
    
    @Test
    public void createTypes() {
        Class<?> employeeClass = dynamicHelper.getDynamicClassLoader().createDynamicClass(
            PACKAGE_PREFIX + ".Employee");
        empBuilder = new JPADynamicTypeBuilder(employeeClass, null, EMP_TABLE_NAME);
        empBuilder.setPrimaryKeyFields("id");
        empBuilder.addDirectMapping("id", int.class, "id");
        empBuilder.addDirectMapping("name", String.class, "name");
        Class<?> deptClass = dynamicHelper.getDynamicClassLoader().createDynamicClass(
            PACKAGE_PREFIX + ".Dept");
        depBuilder = new JPADynamicTypeBuilder(deptClass, null, DEPT_TABLE_NAME);
        depBuilder.setPrimaryKeyFields("depid");
        depBuilder.addDirectMapping("depid", int.class, "depid");
        depBuilder.addDirectMapping("depname", String.class, "depname");
        depBuilder.addOneToManyMapping("empinfo.depinfo", empBuilder.getType(), "depid");
        empBuilder.addOneToOneMapping("empinfo.depinfo", depBuilder.getType(), "depid");
        dynamicHelper.addTypes(true, false, empBuilder.getType(), depBuilder.getType());
    }

    @Test
    public void populate() {
        Server serverSession = JpaHelper.getServerSession(emf);
        queryTracker = QuerySQLTracker.install(serverSession);
        queryTracker.reset();
        DynamicEntity emp = dynamicHelper.newDynamicEntity(empBuilder.getType().getName());
        emp.set("id", 123);
        emp.set("name", "Rohit Banga");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        em.persist(emp);
        em.getTransaction().commit();
        em.close();
        assertEquals(1, queryTracker.getTotalCalls("INSERT"));

        queryTracker.reset();
        DynamicEntity dep = dynamicHelper.newDynamicEntity(depBuilder.getType().getName());
        dep.set("depid", 1010);
        dep.set("depname", "Oracle Server Technologies");
        em = emf.createEntityManager();
        em.getTransaction().begin();
        em.persist(dep);
        em.getTransaction().commit();
        em.close();
        assertEquals(1, queryTracker.getTotalCalls("INSERT"));
    }

    @Test
    public void setDept() {
        queryTracker.reset();
        EntityManager em = emf.createEntityManager();
        Query query1 = em.createQuery("SELECT e FROM Employee e WHERE e.id = '123'");
        DynamicEntity emp = (DynamicEntity)query1.getSingleResult();
        Query query2 = em.createQuery("SELECT d FROM Dept d WHERE d.depid = '1010'");
        DynamicEntity dept = (DynamicEntity)query2.getSingleResult();
        emp.set("empinfo.depinfo", dept);
        em.getTransaction().begin();
        em.persist(emp);
        em.getTransaction().commit();
        em.close();
        assertEquals(1, queryTracker.getTotalCalls("UPDATE"));
    }
    
    static class MyPersistenceProviderResolver implements PersistenceProviderResolver {
        public List<PersistenceProvider> getPersistenceProviders() {
            ArrayList<PersistenceProvider> providers = new ArrayList<PersistenceProvider>();
            providers.add(new MyPersistenceProvider());
            return providers;
        }
        public void clearCachedProviders() {           
        }
    }
    
    static class MyPersistenceProvider implements PersistenceProvider {
        public EntityManagerFactory createContainerEntityManagerFactory(
            PersistenceUnitInfo info, @SuppressWarnings("rawtypes")Map map) {
            return null;
        }
        public EntityManagerFactory createEntityManagerFactory(String emName,
            @SuppressWarnings("rawtypes")Map map) {
            SEPersistenceUnitInfo puInfo = new SEPersistenceUnitInfo();
            URL noopURL = null;
            try {
                noopURL = new URL(null, "noop:", new URLStreamHandler() {
                    protected URLConnection openConnection(URL url) throws IOException {
                        return new URLConnection(url) {
                            public InputStream getInputStream() throws IOException {
                                return null;
                            }
                            public void connect() throws IOException {
                            }
                        };
                    }
                });
            }
            catch (Exception e) {
                e.printStackTrace();
            }
            puInfo.setPersistenceUnitRootUrl(noopURL);
            EntityManagerSetupImpl entityManagerSetupImpl = 
                new EntityManagerSetupImpl(PERSISTENCE_UNIT_NAME, PERSISTENCE_UNIT_NAME);
            entityManagerSetupImpl.predeploy(puInfo, map);
            return new EntityManagerFactoryImpl(entityManagerSetupImpl, map);
        }
        public ProviderUtil getProviderUtil() {
            return null;
        }
     }


    /**
     * This custom SessionLog implementation wraps the existing one and
     * redirects all SQL calls to the tracker. All messages are also passed to
     * the original tracker.
     */
    static class SQLTrackingSessionLog extends DefaultSessionLog {
        protected QuerySQLTracker tracker;
        protected SessionLog originalLog;
        protected SQLTrackingSessionLog(Session session, QuerySQLTracker aTracker) {
            this.tracker = aTracker;
            this.originalLog = session.getSessionLog();
            setSession(session);
            setWriter(this.originalLog.getWriter());
        }
        public QuerySQLTracker getTracker() {
            return this.tracker;
        }
        public synchronized void log(SessionLogEntry entry) {
            if (entry.getNameSpace() != null && entry.getNameSpace().equalsIgnoreCase(SessionLog.SQL)) {
                getTracker().getCurrentResult().addSQL(entry.getMessage());
            }
            if (!this.originalLog.shouldLog(entry.getLevel())) {
                return;
            }
            this.originalLog.log(entry);
        }
    }

    static class QuerySQLTracker extends SessionEventAdapter {
        protected List<QueryResult> queries;
        /**
         * Constructs and installs the event listener and sql tracking session log
         * 
         * @param session
         */
        private QuerySQLTracker(Session session) {
            session.getEventManager().addListener(this);
            session.setSessionLog(new SQLTrackingSessionLog(session, this));
            reset();
        }
        public static QuerySQLTracker install(Session session) {
            if (session.getSessionLog() instanceof SQLTrackingSessionLog) {
                return ((SQLTrackingSessionLog)session.getSessionLog()).getTracker();
            }
            return new QuerySQLTracker(session);
        }
        public static void uninstall(Session session) {
            if (session.getSessionLog() instanceof SQLTrackingSessionLog) {
                SQLTrackingSessionLog trackingLog = (SQLTrackingSessionLog) session.getSessionLog();
                QuerySQLTracker tracker = trackingLog.getTracker();
                session.getEventManager().removeListener(tracker);
                session.setSessionLog(trackingLog.originalLog);
            }
        }
        /**
         * Helper method to retrieve a tracker from a session where it was installed
         * If the session exists but does not have a tracler installed then an
         * exception is thrown.
         */
        public static QuerySQLTracker getTracker(Session session) {
            if (session == null) {
                return null;
            }
            SessionLog sessionLog = session.getSessionLog();

            if (sessionLog instanceof SQLTrackingSessionLog) {
                return ((SQLTrackingSessionLog)sessionLog).getTracker();
            }
            throw new RuntimeException("Could not retrieve QuerySQLTracker from session: " + session);
        }
        /**
         * Reset the lists of SQL and queries being tracked
         */
        public void reset() {
            this.queries = new ArrayList<QueryResult>();
        }
        public List<QueryResult> getQueries() {
            return this.queries;
        }
        protected QueryResult getCurrentResult() {
            if (getQueries().size() == 0) {
                getQueries().add(new QueryResult(null));
                // throw new RuntimeException("Received SQL without a Query ???");
            }
            return getQueries().get(getQueries().size() - 1);
        }
        public int getTotalSQLCalls() {
            int totalSQLCalls = 0;
            for (QueryResult result : getQueries()) {
                totalSQLCalls += result.sqlStatements.size();
            }
            return totalSQLCalls;
        }
        public int getTotalCalls(String startsWith) {
            int calls = 0;
            for (QueryResult result : getQueries()) {
                String sub = result.resultString.substring(0, startsWith.length());
                if (sub.equalsIgnoreCase(startsWith)) {
                    calls++;
                }
            }
            return calls;
        }
        public int getTotalSQLCalls(String startsWith) {
            int sqlCalls = 0;
            for (QueryResult result : getQueries()) {
                for (String sql : result.sqlStatements) {
                    String sub = sql.substring(0, startsWith.length());
                    if (sub.equalsIgnoreCase(startsWith)) {
                        sqlCalls++;
                    }
                }
            }
            return sqlCalls;
        }
        public int getTotalSQLSELECTCalls() {
            return getTotalSQLCalls("SELECT");
        }
        public int getTotalSQLINSERTCalls() {
            return getTotalSQLCalls("INSERT");
        }
        public int getTotalSQLUPDATECalls() {
            return getTotalSQLCalls("UPDATE");
        }
        public int getTotalSQLDELETECalls() {
            return getTotalSQLCalls("DELETE");
        }
        public void preExecuteQuery(SessionEvent event) {
            QueryResult result = new QueryResult(event.getQuery());
            getQueries().add(result);
        }
        public void postExecuteQuery(SessionEvent event) {
            if (getCurrentResult().query == null) {
                getCurrentResult().setQuery(event.getQuery());
            }
            getCurrentResult().setResult(event.getResult(), event.getSession());
        }
        class QueryResult {
            private DatabaseQuery query;
            private String resultString = null;
            public List<String> sqlStatements = new ArrayList<String>();
            QueryResult(DatabaseQuery q) {
                query = q;
            }
            protected void setQuery(DatabaseQuery query) {
                this.query = query;
            }
            protected void setResult(Object queryResult) {
                setResult(queryResult, null);
            }
            @SuppressWarnings("unchecked")
            protected void setResult(Object queryResult, Session session) {
                StringWriter writer = new StringWriter();
                writer.write(Helper.getShortClassName(query));
                writer.write("[" + System.identityHashCode(query) + "]");
                writer.write(" result = ");

                Object result = queryResult;
                if (queryResult instanceof Collection) {
                    result = ((Collection) queryResult).toArray();
                }

                if (result == null) {
                    writer.write("NONE");
                } else {
                    if (result instanceof Object[]) {
                        Object[] results = (Object[]) result;
                        writer.write("<" + results.length + "> [");
                        for (int index = 0; index < results.length; index++) {
                            if (index > 0) {
                                writer.write(", ");
                            }
                            boolean writePkOnly = false;
                            Object object = results[index];
                            // if session is provided then may extract pk from
                            // object
                            if (session != null) {
                                if (object instanceof FetchGroupTracker) {
                                    FetchGroupTracker tracker = (FetchGroupTracker) object;
                                    // object.toString may trigger loading of the
                                    // whole object. To avoid that write the pk
                                    // only.
                                    if (tracker._persistence_getFetchGroup() != null) {
                                        writePkOnly = true;
                                    }
                                }
                            }
                            if (writePkOnly) {
                                writer.write(Helper.getShortClassName(object) + "("
                                        + session.getDescriptor(object.getClass()).getObjectBuilder().extractPrimaryKeyFromObject(object, (AbstractSession) session) + ")");
                            } else {
                                writer.write(object + "");
                            }
                        }
                        writer.write("]");
                        resultString = writer.toString();
                    } else {
                        writer.write(result.toString());
                    }
                }
                this.resultString = writer.toString();
            }
            public void addSQL(String sql) {
                sqlStatements.add(sql);
            }
            public String toString() {
                if (this.resultString == null) {
                    setResult(null);
                }
                return this.resultString;
            }
        }
    }
}

Back to the top