Hi,

I have a very peculiar problem. I'm using JPA/openJPA in conjunction with
dbUnit for unit testing.

The sequence of events of course is
Initialize db using dbunit API -> Do insert using openJPA -> do asserts
using dbunit API.

In order to have a decent performance level over what would be a sizeable
test suite, I'm opting to reuse a JDBC connection, the one I use for dbUnit.
This connection is created and managed by my code.

This means however that the JDBC connection is live while openJPA is doing
its own inserts (using its own connection which it would get from my
persistence.xml settings and managed by openJPA).

I find that in this scenario, openJPA behaves very strangely in that it is
not able to recognize inheritance hierarchies properly (trying to insert the
derived class object multiple times instead of inserting the base class and
then derived class) and other such inexplicable issues.

I find that if i do the dbunit stuff and then close my managed connection,
openJPA starts behaving perfectly fine.

I'm sure there's something I'm doing wrong here because I really can't find
an explaination for this behaviour even if there are two live connections
(think of it as a connection pool). Maybe the answer lies in the fact that I
use "ThreadLocal" to store an entity manager object.

Although, I don't wish to probably point in the wrong direction I'd also
want to mention a small thing of interest. The same piece of code seems to
work perfectly on mysql but fail on DB2 Express C. (sometimes I think is it
the Isolation levels?)




=======================THE CLIENT CLASS
(TEST)====================================

package dk.pbs.bs.dao.unittests;

import java.util.Date;

import javax.persistence.EntityManager;

import org.dbunit.Assertion;
import org.dbunit.DatabaseTestCase;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.DefaultDataSet;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.operation.DatabaseOperation;

import dk.pbs.bs.dao.BaseDAO;
import dk.pbs.bs.domain.Country;
import dk.pbs.bs.domain.Creditor;
import dk.pbs.bs.domain.Customer;
import dk.pbs.bs.utils.PersistenceBootstrapper;
import dk.pbs.bs.utils.unittestutils.DBUnitHelper;

public class BaseDAOTest extends DatabaseTestCase {
        
        @Override
        protected void setUp() throws Exception {
                //do nothing
        }
        
        @Override
        protected IDatabaseConnection getConnection() throws Exception {
                return DBUnitHelper.getConnection();
        }
        
        //Implementation note: Make sure the default dataset is always the one
        //that's the most used.
        @Override
        protected IDataSet getDataSet() throws Exception {
                return new FlatXmlDataSet(
                        
ClassLoader.getSystemResourceAsStream("BaseDAOTest1.xml"));
        }
        
        //IMP: Make sure your test documentation is verbose and describes the
        // purpose of the unit test.
        /**
         * Creation of an object. Simple object. Try creating two objects at 
once.
         * 
         * Normal flow
         */
        public void testCreate() {
                EntityManager manager;
                Customer customer = new Customer(new Double(123), new 
Double(123), 
                                new String("DK1"), new Date(), new Date(),
                                new Date(), "KMD", new Date()); 
                Customer anotherCustomer = new Customer(new Double(124), new 
Double(124), 
                                new String("DK2"), new Date(), new Date(),
                                new Date(), "ATP", new Date());
                try {
                        //Use appropriate operations to get the database in the 
state that
                        //you require.
                        //TODO: Very imp....How to get it to cascade if req??
                        DatabaseOperation.DELETE.execute(getConnection(), 
getDataSet());
                        
                        //Invocations to the processing logic
                        manager = PersistenceBootstrapper.getEntityManager();
                        manager.getTransaction().begin();
                        BaseDAO.create(manager, customer);
                        BaseDAO.create(manager, anotherCustomer);
                        manager.getTransaction().commit();
                        
                        //Do the asserts... Result verification.
                        ITable addedData = getConnection().createQueryTable(
                                                "customer", 
                                                "select pbs_no, cvr_no, prefix, 
" +
                                                "postal_cd, update_by from 
customer where" +
                                                " pbs_no in (123, 124)");
                        Assertion.assertEquals(
                                                                new 
DefaultDataSet(addedData), getDataSet());
                } catch (Exception e) {
                        e.printStackTrace();
                        assertEquals("Failed", "Normal flow failed for 
BaseDAOTest", 
                                                        e.getMessage());
                } finally {
                        //Do this instead of closing the manager directly on 
your own.
                        PersistenceBootstrapper.closeManager();
                }
        }
        
        /**
         * Creation of an object. Inheritance hierarchy.
         * 
         * Normal flow
         */
        public void testCreateInheritance() {
                EntityManager manager;
                Creditor creditor = new Creditor(new Double(125), new 
Double(125), 
                                "DK3", new Date(), new Date(), new Date(), 
"PROG", new Date());         
                try {
                        //Note the new dataset
                        IDataSet expectedDataset = new FlatXmlDataSet(
                                        
ClassLoader.getSystemResourceAsStream("BaseDAOTest2.xml"));
                        
                        DatabaseOperation.DELETE.execute(getConnection(), 
expectedDataset);
                        
                        //Invocations to the processing logic
                        manager = PersistenceBootstrapper.getEntityManager();
                        manager.getTransaction().begin();
                        BaseDAO.create(manager, creditor);
                        manager.getTransaction().commit();
                        
                        //Do the asserts... Result verification.
                        //Note the way the actual dataset is created.
                        QueryDataSet actual = new QueryDataSet(getConnection());
                        actual.addTable("customer", "select pbs_no, cvr_no, 
prefix, " +
                                        "postal_cd, update_by from customer 
where" +
                                        " pbs_no = 125");
                        actual.addTable("creditor", "select pbs_no, cred_type, 
" +
                                        "billing_type, update_by from creditor 
where" +
                                        " pbs_no = 125");
                        
                        //compare with expected dataset.
                        Assertion.assertEquals(actual, expectedDataset);
                } catch (Exception e) {
                        e.printStackTrace();
                        assertEquals("Failed", "Normal flow failed for 
BaseDAOTest", 
                                                        e.getMessage());
                } finally {
                        //Do this instead of closing the manager directly on 
your own.
                        PersistenceBootstrapper.closeManager();
                }
        }
        
        /**
         * Creation of an object. Inheritance + Many-One mapping. Deeper object
         * graph.
         * 
         * Normal flow
         */
        //Note comments for country.
        public void testCreateManyToOne() {
                EntityManager manager;
                Creditor creditor = new Creditor(new Double(125), new 
Double(125), 
                                "DK3", new Date(), new Date(), new Date(), 
"PROG", new Date());
                Country creditorsCountry = new Country("DK", "Denmark", "PROG");
                
                creditor.setCountry(creditorsCountry);
                
                try {
                
                        IDataSet dataset = new FlatXmlDataSet(
                        
ClassLoader.getSystemResourceAsStream("BaseDAOTest3.xml"));
                        
                        DatabaseOperation.DELETE.execute(getConnection(), 
dataset);
                        
                        //Invocations to the processing logic
                        manager = PersistenceBootstrapper.getEntityManager();
                        manager.getTransaction().begin();
                        BaseDAO.create(manager, creditor);
                        manager.getTransaction().commit();
                        
                        //Do the asserts... Result verification.
                        QueryDataSet actual = new QueryDataSet(getConnection());
                        actual.addTable("customer", "select pbs_no, cvr_no, 
prefix, " +
                                        "postal_cd, update_by from customer 
where" +
                                        " pbs_no = 125");
                        actual.addTable("creditor", "select pbs_no, cred_type, 
" +
                                        "billing_type, update_by from creditor 
where" +
                                        " pbs_no = 125");
                        actual.addTable("country_master", "select country_cd, 
country, " +
                                        "update_by from country_master where" +
                                        " country_cd = \"DK\"");
                        
                                
                        Assertion.assertEquals(actual, dataset);
                } catch (Exception e) {
                        e.printStackTrace();
                        assertEquals("Failed", "Normal flow failed for 
BaseDAOTest", 
                                                        e.getMessage());
                } finally {
                        //Do this instead of closing the manager directly on 
your own.
                        PersistenceBootstrapper.closeManager();
                }
        }
}
===============================================================================

=========================The HELPER CLASSES
===================================


package dk.pbs.bs.utils.unittestutils;

import java.sql.SQLException;
import java.util.HashMap;

import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.datatype.IDataTypeFactory;
import org.dbunit.ext.db2.Db2DataTypeFactory;
import org.dbunit.ext.mssql.MsSqlDataTypeFactory;
import org.dbunit.ext.mysql.MySqlDataTypeFactory;
import org.dbunit.ext.oracle.OracleDataTypeFactory;

import dk.pbs.bs.frameworks.exceptions.TechnicalException;
import dk.pbs.bs.utils.PersistenceBootstrapper;

/**
 * Convenience class for DBunit related operations.
 * 
 * Jul 21, 2008
 */
public class DBUnitHelper {
        
        /** Reuse the same connection for all tests      */
        private static IDatabaseConnection connection;
        
        private static HashMap<String, IDataTypeFactory> supportedDBs;
        
        static {
                supportedDBs = new HashMap<String, IDataTypeFactory>(4);
                supportedDBs.put("mysql", new MySqlDataTypeFactory());
                supportedDBs.put("mssql", new MsSqlDataTypeFactory());
                supportedDBs.put("oracle", new OracleDataTypeFactory());
                supportedDBs.put("db2/nt", new Db2DataTypeFactory());
        }
        
        /**
         * Gets a connection to the database.
         * 
         * @return                                                              
A dbunit connection object
         * @throws      TechnicalException                      If the 
underlying connection cannot
         *                                                                      
        be obtained.
         */
    public static IDatabaseConnection getConnection() 
        throws TechnicalException {
        try {
                if (connection == null || 
connection.getConnection().isClosed()) {
                        connection = new DatabaseConnection(
                                                                
PersistenceBootstrapper.getRawConnection());
                    String dbname = 
                                        connection.getConnection().getMetaData()
                                                                                
        .getDatabaseProductName();
                    connection.getConfig().setProperty(
                                DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
                         supportedDBs.get(dbname.toLowerCase()));
                }
            return connection;          
        } catch (SQLException e) {
                throw new TechnicalException("Unable to find database product", 
e);
        }
    }
    
        
        @Override
        protected void finalize() throws Throwable {
                super.finalize();
                if (!connection.getConnection().isClosed()) {
                        connection.getConnection().close();
                        connection = null;
                }
        }
}


====================


package dk.pbs.bs.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import dk.pbs.bs.frameworks.exceptions.TechnicalException;

/**
 * Utility class that handles the creation of an entity manager factory in
an 
 * un-managed environment. Dispenses entity managers.
 * Also able to dispense raw [EMAIL PROTECTED] Connection} objects that might be
required
 * for some operations where it is necessary to get the underlying
connection
 * to the database and access through JPA is not the best option.
 * The class requires bootstrap properties to have been set in a properties
 * file called abc.properties that must be available on the
 * classpath.
 * 
 *
 * Jul 17, 2008
 */
//TODO: Make this class environment aware?? So that it works in a managed
//environment also.
public class PersistenceBootstrapper {
        
        private static EntityManagerFactory factory;
        
        private static PersistenceBootstrapper thisObject;
        
        /** The manager associated with the current thread */
        private static ThreadLocal<EntityManager> currentManager = null;
                
        //TODO: Fix this... Get them from Enums
        private static final String BS_PROP_FILE = "abc";
        
        private static final String BS_PERSISTENT_CONTEXT = 
"jpa.persistence.unit";
        
        private static final String BS_CONNECTION_URL = "db.connection.url";
        
        private static final String BS_CONNECTION_USERNAME = 
"db.connection.uname";
        
        private static final String BS_CONNECTION_PASSWORD = 
                                                                                
                        "db.connection.password";
        
        private static final String BS_CONNECTION_DRIVER = 
"db.connection.driver";
        
        /** The properties set */
        private static ResourceBundle BS_PROPS;
        
        /* Force singleton       */
        private PersistenceBootstrapper() {
                BS_PROPS = ResourceBundle.getBundle(BS_PROP_FILE);
                String persistentUnitName = (String) 
                                                                        
BS_PROPS.getObject(BS_PERSISTENT_CONTEXT);
                factory = 
Persistence.createEntityManagerFactory(persistentUnitName);
        }
        
        private static synchronized void initialize() {
                if (thisObject == null) {
                        thisObject = new PersistenceBootstrapper();
                }
        }
        
        /**
         * Get an entity manager. If you have already obtained an entity manager
and
         * not closed it using remove, this method will return the previously
         * supplied [EMAIL PROTECTED] EntityManager} and not create a new one . 
To get a new
         * Entity manger, close the earlier one first using the 
         * [EMAIL PROTECTED] EntityManager#close()} method.
         * 
         * @return                              An entity manager.
         */
        public static EntityManager getEntityManager() {
                initialize();
                if (currentManager == null || !currentManager.get().isOpen())  {
                        currentManager = null; //remove dead managers due to 
direct close
                        currentManager = new ThreadLocal<EntityManager>();
                        currentManager.set(factory.createEntityManager());
                }
                return currentManager.get();
        }
        
        /**
         * Closes the entity manager associated with this thread.
         */
        public static void closeManager() {
                if (currentManager != null && currentManager.get().isOpen()) {
                        currentManager.get().close();
                        currentManager.remove();
                        currentManager = null;
                }
        }
        
        //TODO: Isn't there a way to get handle to persistence.xml??
        //If yes, get properties from there...
        public static Connection getRawConnection() throws TechnicalException {
                Connection con;
                String driver;
                String userName;
                String password;
                String url;
                
                initialize();
                try {
                        driver = 
                                (String) 
PersistenceBootstrapper.BS_PROPS.getObject(
                                                                                
                                BS_CONNECTION_DRIVER);
                        url = 
                                (String) 
PersistenceBootstrapper.BS_PROPS.getObject(
                                                                                
                                        BS_CONNECTION_URL);
                        userName = 
                                (String) 
PersistenceBootstrapper.BS_PROPS.getObject(
                                                                                
                                BS_CONNECTION_USERNAME);
                        password = 
                                (String) 
PersistenceBootstrapper.BS_PROPS.getObject(
                                                                                
                                BS_CONNECTION_PASSWORD);
                } catch (MissingResourceException m) {
                        throw new TechnicalException(
                                        "The property " + m.getKey() + " is not 
set", m);
                }
        try {
                Class.forName(driver);
                con = DriverManager.getConnection(url, userName, password);
        } catch (Exception e) {
                throw new RuntimeException(
                "Unable to connect. Is your driver class jar on the 
classpath?", e);
        }
        return con;
        }
}

============================================PERSISTENCE.XML
<?xml version="1.0" encoding="UTF-8"?>

<persistence xmlns="http://java.sun.com/xml/ns/persistence";
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
    version="1.0">
    
    <persistence-unit name="refimpl-container" transaction-type="JTA">
        <provider>
            org.apache.openjpa.persistence.PersistenceProviderImpl
        </provider>
         <jta-data-source>bsds</jta-data-source>
        <non-jta-data-source>NoTxDatasource</non-jta-data-source>
        <properties>
                <property name="openjpa.jdbc.DBDictionary" value="db2"/>
            <property name="openjpa.jdbc.Schema" value="PBSDEV1"/>
           <property name="openjpa.jdbc.SynchronizeMappings" value="false"/> 
            <property name="openjpa.Log" 
                value="Runtime=TRACE, Metadata=TRACE, Tool=TRACE, SQL=TRACE"/>
        </properties>
    </persistence-unit>
    <persistence-unit name="refimpl-nocontainer" 
                                                                                
transaction-type="RESOURCE_LOCAL">
        <provider>
            org.apache.openjpa.persistence.PersistenceProviderImpl
        </provider>
        <class>dk.pbs.bs.domain.Customer</class>
        <class>dk.pbs.bs.domain.Country</class>
         <class>dk.pbs.bs.domain.Creditor</class>
        <properties>
                <property name="openjpa.ConnectionUserName" value="root"/>
                        <property name="openjpa.ConnectionPassword" 
value="root"/>
                        <property name="openjpa.ConnectionURL" 
                                                                        
value="jdbc:mysql://localhost:3306/pbsdev"/>
                        <property name="openjpa.ConnectionDriverName" 
                                                                                
        value="org.gjt.mm.mysql.Driver"/>
                        <property name="openjpa.ConnectionFactoryProperties"
                                                        
value="PrettyPrint=true, PrettyPrintLineLength=80"/>
                <property name="openjpa.jdbc.DBDictionary" value="mysql"/>
            <property name="openjpa.jdbc.Schema" value="pbsdev"/>
            <property name="openjpa.jdbc.SynchronizeMappings"
value="false"/> 
            <property name="openjpa.Log" 
                value="Runtime=TRACE, Metadata=TRACE, Tool=TRACE, SQL=TRACE"/>
        </properties>
    </persistence-unit>
</persistence>

======================================================







-- 
View this message in context: 
http://n2.nabble.com/open-connections-causing-strange-behaviour-tp661614p661614.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Reply via email to