Help

Inactive Bloggers

Had some time to package the Getting Started example code and put it on the CaveatEmptor Website. So all you MEAP readers have something you can actually run without copy/pasting from the PDFs. I should probably mention that the unedited draft you are reading with MEAP is not completely in sync with the code I just packaged. Well, mostly the Ant build files are a little different, so that shouldn't be a big problem.

Working on the CaveatEmptor main application package now, hopefully ready in a week or two.

Finally, the Manning Early Access Program (MEAP) has the PDFs of /Java Persistence with Hibernate/ online. You can get access on the book website. Some caveats:

  • Manning used a draft from May/June 2006 to create the MEAP chapters. Since then these chapters went through reviews and edits, quite extensive actually. If you want to provide feedback, please note that any typos, code examples that don't match the illustrations, or even broken code examples are most likely already fixed. This makes this public review less useful than it could have been, but what can you do...
  • If you know /Hibernate in Action/ you might be surprised that the first chapters are quite similar to what was in this old edition. This is normal, ORM is still ORM and POJOs are still POJOs, so much of that content hasn't changed (although it was certainly updated whenever necessary). Expect more interesting new stuff after chapter 4, there are 12 more left then.
  • Some terms are wrong in these older drafts, for example, I have used entity bean until June when I realized that Sun marketing wants to call these things entities now in EJB 3.0. Do a mental search and replace.

I want to upload the source code and the examples for the Getting Started chapter as soon as possible, the main CavatEmptor source still needs some work though. Anyway, I will put the Getting Started on the CaveatEmptor website as soon as new releases of Hibernate, HAN, and HEM are out. Which should be any day now, so stay tuned. I'll post again next week.

23. Aug 2006, 21:33 CET, by Max Andersen

Hibernate Tools 3.2.0.beta7 (http://tools.hibernate.org) have been made available.

Many-to-Many

One new major feature did go into this release (besides bugfixes and minor enhancements). Namely support for many-to-many tables during reverse engineering; so please go check that out if you have been waiting for it and give feedback at our forum and bugreports or enhancement requests at our JIRA .

See screenshots and other noteworthy news here for more information.

Query editing viewlet

I sat down and created a small viewlet of the HQL/JPAQL editing features we added in the previous release. That viewlet can be seen here , and one of the features it illustrates is shown in the following picture:

http://www.hibernate.org/hib_docs/tools/eclipse/newandnoteworthy/completeinsidejava.png

p.s. to enable this feature, you need an open Hibernate Console configuration and remember to enable Hibernate for you java project in the properties of your project.

I've seen lot's of people writing (or using) third party abstraction frameworks on top of ORM solutions like Hibernate so that they can potentially get rid of one ORM engine to another (or even an other technology). Such frameworks are even considered by some architectsas a must-have.

Praiseworthy, but raising more issues than fixing ones. When you abstract technologies like Hibernate to make them portable, you end up sacrificing lot's of power and functionalities. Simply because semantics are slightly different from one engine to an other.

Let's play with statistics:

  • in 1% of the applications, the need for a switch from one ORM engine to an other arise
  • in 99% of the applications, the need for one or several advanced features of ORM arise

You should focus on using and knowing your ORM engine and not on spending time in writing (or using) an abstraction framework.

When using abstraction frameworks you haven't written, other kind of problems actually show up.

When your ORM engine introduce new APIs or new features, you have to wait for the abstraction framework to release accordingly making you dependent on someone else's agenda.

You no longer know (either by heart or at all) the actual method abstraction (or semantic alteration) introduced on top of your ORM engine. This user learnt it the hard way (and he is unfortunately not alone), Spring's /HibernateTemplate/ wraps the Hibernate methods without even giving you a hint about the actual semantic.

The only proper abstraction between an ORM engine and another is a common specification they adhere to. This is one of the reasons why JPA (EJB 3.0) has been introduced. Why is it the only way? Because a specification describes in a very detailed way the common semantic the various ORM engines have to follow.

So please, think twice before using some random proprietary abstraction framework (open source or not). They usually bring no value add but increasing bug areas... Specifications are here to solve the engine abstraction problem (but once again, this is a concern in very few applications).

PS in case someone is misleading, I'm not arguing against DAO layers

I was looking for a good way to integrate DBUnit with Seam, so that I can prepare a dataset for functional testing without too much hassle. This is what I came up with, a test superclass that integrates with Seam and adds DBUnit operations that run before and after every test method.

The org.jboss.seam.mock.SeamTest class is the base class you'd usually use for functional testing in a Seam application. It allows you to easily script an interaction (for a whole session or a single event) at the presentation layer level. I extended this class to add some DBUnit specific features:

/**
 * Uses a JNDI datasource and JTA for DBUnit operations.
 *
 * @author christian.bauer@jboss.com
 */
public abstract class SeamDBUnitTest extends SeamTest {

    private static Log log = LogFactory.getLog(SeamDBUnitTest.class);

    private Context ctx;

    private ReplacementDataSet dataSet;

    @Configuration(beforeTestClass = true)
    public void prepare() throws Exception {
        log.debug("Preparing DBUnit setup for test class");

        // Prepare JNDI context
        ctx = new InitialContext();

        // Load the base dataset file
        URL input = Thread.currentThread()
                        .getContextClassLoader().getResource(getDataSetLocation());
        if (input == null)
            throw new RuntimeException(
                "Dataset '" + getDataSetLocation() + "' not found in classpath"
            );
        dataSet = new ReplacementDataSet(
                        new FlatXmlDataSet(input.openStream())
                      );
        dataSet.addReplacementObject("[NULL]", null);
    }

    @Configuration(beforeTestMethod = true)
    public void beforeTestMethod() throws Exception {
        executeOperations(getBeforeTestMethodStack());
    }

    @Configuration(afterTestMethod = true)
    public void afterTestMethod() throws Exception {
        executeOperations(getAfterTestMethodStack());
    }

    private void executeOperations(DatabaseOperation[] operations) throws Exception {
        log.debug("Executing database operations in JTA transaction");

        UserTransaction tx = getUserTransaction();
        tx.begin();

        IDatabaseConnection con = new DatabaseConnection( getConnection() );

        // TODO: Remove this once DBUnit works with HSQL DB
        DatabaseConfig config = con.getConfig();
        config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new FixDBUnit());

        for (DatabaseOperation op : operations) {
            op.execute(con, dataSet);
        }
        tx.commit();
    }

    // Subclasses can/have to override the following methods

    /**
     * Return a JDBC <tt>Connection</tt> with disabled foreign key checking.
     */
    protected Connection getConnection() throws Exception {
        log.debug("Getting database connection through JNDI lookup of DataSource");

        // Lookup Datasource in JNDI
        DataSource ds = (DataSource)ctx.lookup(getDataSourceJNDIName());
        Connection con = ds.getConnection();

        // Disable foreign key constraint checking
        // This really depends on the DBMS product... here for HSQL DB
        con.prepareStatement("set referential_integrity FALSE")
            .execute();

        return con;
    }


    /**
     * Add <tt>DatabaseOperation</tt> that run before every test method.
     */
    protected DatabaseOperation[] getBeforeTestMethodStack() {
        return new DatabaseOperation[]{};
    }

    /**
     * Add <tt>DatabaseOperation</tt> that run after every test method.
     */
    protected DatabaseOperation[] getAfterTestMethodStack() {
        return new DatabaseOperation[]{};
    }

    /**
     * The relative location of the data set on the classpath.
     */
    protected abstract String getDataSetLocation();

    /**
     * The JNDI name of the datasource used by DBUnit.
     */
    protected abstract String getDataSourceJNDIName();

}

This is pretty simple stuff: For each test class in the test suite I load a configured DBUnit dataset from the classpath and replace all the NULL markers with real nulls (see DBUnit documentation). Before each test method, which in a Seam functional test has the scope of a session, I run a stack of DBUnit operations, and again after each test method for cleanup. So each session in my functional tests works with a defined dataset. A concrete test subclass has to provide some settings, such as the location of the DBUnit dataset file, and the name of the database connection that is used. A subclass can also override the getConnection() method if a custom routine is needed. (Also note the TODO for a DBUnit/HSQL issue, one day they might actually fix this.)

A DBUnit dataset might look like this:

<?xml version="1.0"?>

<dataset>
    <USERS      USER_ID             ="1"
                OBJ_VERSION         ="0"
                FIRSTNAME           ="Root"
                LASTNAME            ="Toor"
                USERNAME            ="root"
                PASSWORD            ="secret"
                EMAIL               ="root@toor.tld"
                RANK                ="0"
                IS_ADMIN            ="true"
                CREATED             ="2006-06-26 13:45:00"
                HOME_STREET         ="[NULL]"
                HOME_ZIPCODE        ="[NULL]"
                HOME_CITY           ="[NULL]"
                DEFAULT_BILLINGDETAILS_ID ="[NULL]"
            />
</dataset>

Finally, this a concrete functional test class:

public class LoginLogoutFunction extends SeamDBUnitTest {

    protected String getDataSetLocation() {
        return "org/hibernate/ce/modules/user/test/basedata.xml";
    }

    protected String getDataSourceJNDIName() {
        return "[=>java:/caveatemptorDatasource]";
    }

    protected DatabaseOperation[] getBeforeTestMethodStack() {
        return new DatabaseOperation[] {
            DatabaseOperation.CLEAN_INSERT
        };
    }

    @Test
    public void testLoginLogout() throws Exception {

        // Test logged out state
        new Script() {

            // Test a component that requires a logged in user
            @Override
            protected void invokeApplication() {
                assert !isSessionInvalid();
                ChangePassword changePw =
                        (ChangePassword) Component.getInstance("changePassword", true);
                String outcome = changePw.doChange();
                assert "login".equals(outcome);
            }

            // Test if there is no loggedIn flag in the Session context
            @Override
            protected void renderResponse() {
                assert !Manager.instance().isLongRunningConversation();
                assert Contexts.getSessionContext().get("loggedIn") == null;
            }

        }.run();
...

The first three methods implement and override the settings for DBUnit operations, you can create a stack of DBUnit operations that are executed before and after each test method (only one in this example, running before). The test method testLoginLogout() is a single session in which I run several scripts (one for each event) to exercise the login and logout functionality of my application. As you can see, the methods in my script are simulating JSF presentation layer events. To see the full class without the DBUnit additions, browse the Seam tutorials, this is from the Hotel Booking application.

So one test class works with one dataset, I don't think this is too restrictive if you consider TestNG's ability to work with named groups of tests and wildcard matching.

Showing 1031 to 1035 of 1137 blog entries