Posts Tagged ‘java’

Presentation on Maven

Last night I spoke at TechMaine’s Java Users Group about Maven.  I’ve made the slides available on Slideshare, although Slideshare botched some of the formatting a bit.  You will get the proper format if you download it and view it locally.

Enjoy!  Here is the abstract for the presentation if you want to know what it’s about before diving in:

“Why do we need another build tool when we already have Ant? By focusing on convention over configuration, Maven allows you to declaratively define how your project is built, which reduces a lot of the procedural code that you’d need to implement in every build file if you were using Ant. This, along with Maven’s built-in management of repositories for project dependencies, allows you to streamline your build process. Ultimately Maven can reduce the amount of time that would otherwise be wasted hunting down jar files and fiddling with boilerplate build scripts.

This presentation covers Maven’s core concepts. It introduces the Plugin architecture, and explain how the most popular plugins are used. It also covers the POM concept and how it relates to dependency tracking and repositories.”

Tags: , ,
Posted in blog | Comments Off


External Configuration with JBoss

private-propertyIn a project I’m currently working on, I need to make some parameters configurable, and they need to be outside the .war file that I’m deploying. For example, let’s say I’m creating a service which reads data from some other RESTful service. And let’s say that the other RESTful service has two URLs, one for test and one for production. I’d like to be able to deploy my .war, and then edit a file outside of that .war file to configure which URL my service should be using.

My first inclination was to try to do this with a JNDI Environment Entry, so I began researching that approach. However, while the EJB spec states in 20.2.4 that the container must “provide a deployment tool that allows the Deployer to set and modify the values of the enterprise bean’s environment entries” (thanks for finding that one, ipage), JBoss does not seem to have such a facility.

Soon I started to wonder if JNDI wasn’t a bit overkill for what I needed to do, anyway.  I didn’t want to specify my parameters on the command-line; I wanted to simplify deployment and wanted to be able to change these values at runtime without restarting the server. But perhaps a System Property was all I needed.

As it turns out, JBoss has the System Properties Management Service for such things.  Here’s what you need to do:

  1. Make sure properties-plugin.jar is in your ${JBOSS_HOME}/server/<server>/lib directory.
  2. Make sure the properties-service.xml is in your deploy directory (you can find a copy in the “default” server directory)
  3. You now have two options, either edit the URLList to have a comma-separated list of locations of properties files, or you can specify your properties directly in properties-service.xml in the <attribute name=”Properties”> element.

Now, to access your property, all you need to do is call the venerable System.getProperty() method.

Photo Credit: Shelley Gibb

Tags: , ,
Posted in blog | 4 Comments »


Test your EJBs with JMeter

Note: This is my first blog post on my newly upgraded Wordpress blog (prior to thijaguar-tachometer.jpgs, I used Blogger). Hopefully everything goes OK!

Sometimes it’s helpful to do some performance benchmarks on your EJBs. There are a few different ways to do this, but I’ve found that Apache’s JMeter is an excellent tool for benchmarking. Unfortunately, JMeter doesn’t come with a general-purpose sampler for testing arbitrary EJBs. Luckily, it isn’t very difficult to create one.

For this article, I’m using the JBoss application server to host my EJBs. The process for using other containers should be quite similar.

1.) Create a factory to lookup your EJBs.

The first thing that you’ll probably want to do is create a simple singleton factory class to create instances of your EJB client for your test. I use something like the following:

public class MyServiceFactory {
  private static final Log log = LogFactory.getLog(MyServiceFactory.class);
  private static MyService service;
  private static MyServiceFactory me;
 
  private MyServiceFactory() { }
 
  static {
    MyServiceFactory.me = new MyServiceFactory();
  }
 
  public static MyServiceFactory getInstance() {
    return MyServiceFactory.me;
  }
 
  public MyService getService() {
    if (MyService.service == null) {
      // Get the remote interface of the music search service
      try {
        log.info("Loading the service...");
 
        // JNDI the old-fashioned way:
        Context ctx = new InitialContext();
        service = (MyService)ctx.lookup("MyAction/remote");
        if (service == null) {
          log.error("Didn't get the service!");
        }
      } catch (NamingException e) {
        log.error("Error looking up the remote service", e);
        return null;
      }
    }
    return service;
  }
}

2.) Write the test

Next, we’ll need to write the test itself. To do this, we’ll extend the AbstractJavaSamplerClient class in JMeter’s org.apache.jmeter.protocol.java.sampler package. This abstract class has a runTest method that we will override, and this method implements the actual test. We will also override the getDefaultParameters method to provide some reasonable defaults values which will be displayed in JMeter’s GUI application.

package us.mikedesjardins.demo.jmeter;
 
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
 
public class DigitalContentServiceEJBTestSampler extends AbstractJavaSamplerClient {
  public SampleResult runTest(JavaSamplerContext context) {
    SampleResult results = new SampleResult();
    MyService service = MyServiceFactory.getInstance().getService();
 
    results.sampleStart();
    Long param1 = context.getLongParameter("PARAM_1");
    String param2 = context.getStringParameter("PARAM_2");
 
    MyResult result = service.myMethod(param1, param2);
    if (result != null) {
       results.setSuccessful(true);
       results.setResponseCodeOK();
       results.setResponseMessage("'myResult:" + myResult);
    } else {
       results.setSuccessful(false);
    }
    results.sampleEnd();
    return results;
  }
 
  @Override
  public Arguments getDefaultParameters() {
    Arguments args = new Arguments();
    args.addArgument("PARAM_1", "4815162342");
    args.addArgument("PARAM_2", "Iculus");
    return args;
  }
}

3.) Setup JMeter

JMeter’s extra libs directory is ${JMETER_INSTALL_LIB}/lib/ext. Into that directory you will need to copy any jars that your EJB client will need. In you’re using JBoss, you will want to copy the jbossall-client.jar into that directory as well (for the JNDI client and other remoting goodies) – presumably other application servers have similar client jar files available.

When you fire up JMeter, your new sampler should appear in the Samplers menu. Enjoy!

Photo Credit: Bill Jacobus

Tags: , , ,
Posted in blog | 1 Comment »


Don’t Ignore serialVersionUID

Okay,I admit that this one should have totally been obvious to me long ago. But I’m still a bit of a JEE newcomer (been doing it for almost five years), so perhaps I can be forgiven.

If you do a lot of ORM or EJB remoting, you probably deal with a lot of Serializable classes. And you’re probably used to the annoying warning message that you see all the time in your IDE when you’re working with Serializable classes:

The serializable class BlaBlaBla does not declare a static final serialVersionUID field of type long BlaBlaBla.java myProject/src/main/java/us/mikedesjardins/foo/domain/entity line 44

If you’re like me, you roll your eyes and politely add a @SuppressWarnings(”serial”) to the top of the class definition (or, worse, you just shut the warning message off in your IDE altogether. Even I don’t do that!). You reason with yourself that current versions of Java conveniently and automatically compute the serialVersionUID at run-time, so there’s no need to bother with the formality of a version number on your class – it’s just a nuisance holdover from days of Java yore.

IT’S A TRAP!
Now that I’ve found myself well into a new project with this lazy philosophy, I’m starting to run into problems. I have a client of my EJB that uses one of these Serializable objects, and I’m finding that when I make the most trivial changes to my shared classes, I need to compile both the server and the client components. The two components that were supposed to be loosely coupled are now hopelessly intertwined. So I did some further research on how the JVM computes the ad-hoc serialVersionUID at runtime when it isn’t provided.

This article over at JavaWorld does a far better and more thorough job of explaining it than I will. In a nutshell, backward-compatability with respect to serialization and de-serialization is a lot less fragile than the cases that the serialVersionUID generation is protecting you against. That version generation algorithm computes an SHA hash based on the class name, sorted member variables, modifiers, and interfaces.

In reality, serialization and de-serialization generally only breaks when one of the following things happens to your class (from the aforementioned article at JavaWorld):

Ensure Minimal Coupling Between Components
To ensure that your components which use Serialization have minimal runtime dependencies on each other, you have two options:

Photo Credit: Mike Johnson

Tags: , , ,
Posted in blog | 1 Comment »


Configure Spring to automatically re-connect to your EJBs

If you have a service that is a client of a remote EJB, you may have run into the situation where the EJB server shuts down and restarts. When this happens your EJB client may need to be restarted as well, in order to re-discover and reconnect to the EJBs; otherwise you’ll end up with connection exceptions in the client.

If you’re using Spring to autowire your EJB clients, it’s quite easy to configure the service so that the home interface will refresh on connection failures. Note that if you’re using EJB3, you will need to upgrade to at least version 2.5.5 of Spring. There is a bug in earlier versions of Spring which prevented this technique from working with EJB3.

In your spring file, make sure you configure your slsb references to have cache-home disabled, and refresh-home-0n-connect-failure thusly:

<jee:remote-slsb id="myService" jndi-name="MyService/remote"
        business-interface="us.mikedesjardins.services.MyService"
        cache-home="false" lookup-home-on-startup="false"
        home-interface="us.mikedesjardins.services.MyService"
        resource-ref="false" refresh-home-on-connect-failure="true">
    <jee:environment>
           <!-- Include any relevant environment settings here -->
    </jee:environment>
</jee:remote-slsb>

With this, you should be able to restart your EJB hosts without needing to restart your EJB clients!

Photo Credit: Wanko

Tags: , ,
Posted in blog | Comments Off


Clearing Hibernate Second-Level Caches

Recently, I wanted to be able to clear out all of the Hibernate caches via JBoss’s JMX console. I could have taken the easy way out; we’re using EHCache, so it could have been as simple as calling CacheManager.clearAll(). However, that would have tied me to a specific cache provider. We’re still evaluating switching to other cache providers. Ideally, my solution would not be dependent on a specific cache implementation.

Hibernate’s API does provide a simple way to clear specific caches, but does not provide any method for clearing out all of them. Writing your own is fairly straightforward. First, you obtain all of the entity and collection metadata from the session factory. Next you iterate over the entities, and if the object is cached, you clear out all of the caches associated with the persisted class or collection. Here’s the code:

  @PersistenceContext
  private EntityManager em;
 
  public void clearHibernateCache() {
    Session s = (Session)em.getDelegate();
    SessionFactory sf = s.getSessionFactory();
    Map<string,EntityPersister> classMetadata = sf.getAllClassMetadata();
 
    for (EntityPersister ep : classMetadata.values()) {
      if (ep.hasCache()) {
        sf.evictEntity(ep.getCache().getRegionName());
      }
    }
 
    Map<string,AbstractCollectionPersister> collMetadata = sf.getAllCollectionMetadata();
    for (AbstractCollectionPersister acp : collMetadata.values()) {
      if (acp.hasCache()) {
        sf.evictCollection(acp.getCache().getRegionName());
      }
    }
    return;
  }

Now, if we decide to switch to a different cache provider, this code will not need to be re-written. Hopefully we won’t ever change to a different JPA implementaion. :)

Photo Credit: FolkeB

Tags: , , ,
Posted in blog | 1 Comment »


Hibernate Criteria Subqueries: Exists

NOTE: Apologies for the recent blogging hiatus. I just started a new job and haven’t had a lot of time to devote to this blog lately!

While working on a recent project, I came into a situation where I needed to do an “exists” query, using a Criteria-style query. The online documentation for this feature is a little sparse, so I thought I’d share what I did.

The Pizza-shop Data Model (Again)

I keep reusing a data model for a Pizza shop in my posts, and this post will be no different. This data model first appeared in my JPA mapping tutorial. Here’s an ERD of the model again:

Find me Orders with Small Pizzas!
Given this model, what if we needed to find each order that contained a small pizza? Suppose your database had the following data:


As with my earlier posting, the object model has a PizzaOrder class that contains a Set of Pizza objects which correspond to each customer order. Your first inclination might be to do a criteria-within-a-criteria, like this:

Criteria criteria = Criteria.forClass(PizzaOrder.class);
criteria.createCriteria("pizza").add("pizza_size_id",1);
List
 ordersWithOneSmallPizza = criteria.list();

You’d be in for a bit of a surprise, though. While you might expect only two Pizza orders to be returned (namely, orders #1 and #2), you’ll actually have three orders in the result set; because order #2 has two small pizzas in it, order #2 will appear twice in your results!

The reason why this happens is pretty simple, and it becomes clear if you enable Hibernate’s SQL output feature. To locate all of the pizza orders which contain a small pizza, Hibernate needs to do an inner join to the PIZZA table. This is true regardless of whether you’ve mapped the Pizza objects to be fetched lazily; the join is required because of your query criteria, not because of your mappings. Note: it’d be really nice if Hibernate were clever enough to identify from the result set that it had duplicate PIZZA_ORDER records, and build the Set of Pizza objects accordingly, but I suspect that this would be a very difficult thing to do, so I’m not holding my breath.

The Right Way to Do It
What you’re really trying to do is to obtain all Pizza Orders where an associated small pizza exists. In other words, the SQL query that you’re trying to emulate is

SELECT *
  FROM PIZZA_ORDER
 WHERE EXISTS (SELECT 1
                 FROM PIZZA
                WHERE PIZZA.pizza_size_id = 1
                  AND PIZZA.pizza_order_id = PIZZA_ORDER.pizza_order_id)

The way that you do that is by using an “exists” Subquery, like this:

Criteria criteria = Criteria.forClass(PizzaOrder.class,"pizzaOrder");
DetachedCriteria sizeCriteria = DetachedCriteria.forClass(Pizza.class,"pizza");
sizeCriteria.add("pizza_size_id",1);
sizeCriteria.add(Property.forName("pizza.pizza_order_id").eqProperty("pizzaOrder.pizza_order_id"));
criteria.add(Subqueries.exists(sizeCriteria.setProjection(Projections.property("pizza.id"))));
List<pizzaOrder> ordersWithOneSmallPizza = criteria.list();

And voila, the result will contain two PizzaOrders!
Photo Credit: Squeaky Marmot

Tags: , , ,
Posted in blog | 1 Comment »


Hibernate Tools: Tips for Reverse Engineering at the Command Line

One of the ancillary projects of the Hibernate framework is the Hibernate Tools toolset. Using Hibernate Tools, you can automatically generate your mapping files (or, if you prefer, JPA annotations), POJOs, and DDL from your database schema.

I’ve been enamored with the “Convention over Configuration” web frameworks lately (e.g., Grails, Django), and wondered how hard it’d be to reproduce some of their magic ORM functionality using Hibernate Tools. I’ve concluded that I’ve still got a long ways to go, but that I might have some worthwhile tips to share in the meantime.

My progress has been impeded a bit because the documentation for Hibernate Tools is pretty weak. Hibernate Tools was really generated with Eclipse users in mind. A large portion of the documentation is devoted to explaining how to use the IDE. I’m not using Eclipse, and I intend to use the tools at the command-line, and command-line use really isn’t targeted.

0.) Get the tools and dependencies.
I’m still in the dark ages – I use ant instead of maven for builds, and need to track down jar dependencies the old fashioned way. In addition to the usual Hibernate jars, you’ll also need hibernate-tools, freemarker, and jtidy.


1.) Setup an Ant task

This is pretty straightforward, and it is explained fairly well in the documentation. First, define yourself a Hibernate Tools task. Mine looks like this:

<taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask">
  <classpath>
    <fileset refid="hibernate.libs" />
    <fileset refid="hibernatetools.libs" />
    <fileset refid="app.libs" />
    <fileset refid="compile.libs" />
    <pathelement path="${build}" />
    <pathelement path="${basedir}" />
  </classpath>
</taskdef>

Next, create a task that uses our new definition. We’re going to be creating the mapping files and POJOs in this example. It will look something like this:

<target name="gen_hibernate" depends="compile">
  <delete dir="${genhbm}" />
  <mkdir dir="${genhbm}" />
  <hibernatetool>
    <jdbcconfiguration
        configurationfile="${src}/hibernate-connection.cfg.xml"
        revengfile="hibernate.reveng.xml"
        packagename="us.mikedesjardins.data"
        detectmanytomany="true"
        detectoptimisticlock="true" />
    <hbm2hbmxml destdir="${genhbm}" />
    <hbm2java destdir="${genhbm}">
      <property key="jdk5" value="true" />
      <property key="ejb3" value="false" />
    </hbm2java>
  </hibernatetool>
</target>

You’ll note that I’ve created a hibernate configuration file above which only contains connection information. This is because I ran into problems when I tried to use a hibernate configuration with cache configuration elements in it. You’ll also note a reference to a file called hibernate.reveng.xml. This file controls various aspects of the reverse engineering process. Mine is very simple – I’m working with MS SQL Server, and I don’t want it to include any of the system tables which begin with the prefix sys:

<hibernate-reverse-engineering>
  <!-- Eliminate system tables  -->
  <table-filter name="sys.*" exclude="true">
</hibernate-reverse-engineering>

That should be all that you need to do to get started. When you run this ant target, your domain object POJOs and Mappings should end up in the ${genhbm} directory.

2.) First Problem – Table Names
Let’s say you’re in a company that prefixes most of it’s tables with something silly, like “tb_”. In that case, Hibernate tools will happily create all kinds of POJOs for you named TbAccount, TbAddress, etc. This is probably not what you want. Fortunately, Hibernate tools let you provide your own “Reverse Engineering Strategy” class to deal with situations just like this.

First, update your build target in Ant to include a reference to your strategy class as an attribute of the jdbcconfiguration element:

<target name="gen_hibernate" depends="compile">
  <delete dir="${genhbm}" />
  <mkdir dir="${genhbm}" />
  <hibernatetool>
    <jdbcconfiguration
        configurationfile="${src}/hibernate-connection.cfg.xml"
        revengfile="hibernate.reveng.xml"
        packagename="us.mikedesjardins.data"
        detectmanytomany="true"
        detectoptimisticlock="true"
        reversestrategy="us.mikedesjardins.hibernate.CustomReverseEngineeringStrategy"/>
    <hbm2hbmxml destdir="${genhbm}" />
    <hbm2java destdir="${genhbm}">
      <property key="jdk5" value="true" />
      <property key="ejb3" value="false" />
    </hbm2java>
  </hibernatetool>
</target>

Next, we’ll need to create the strategy class. The documentation recommends that you extend the DelegatingReverseEngineeringStrategy class to do this, like this:

package us.mikedesjardins.hibernate;
 
import org.hibernate.cfg.reveng.DelegatingReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.ReverseEngineeringStrategy;
 
public class CustomReverseEngineeringStrategy extends DelegatingReverseEngineeringStrategy {
  public CustomReverseEngineeringStrategy(ReverseEngineeringStrategy delegate {
    super(delegate);
  }
}

Unfortunately, I was unable to find any JavaDoc documentation for the Hibernate Tools classes, so I had to do a bit of trial-and-error. It turns out there is a method called tableToClassName that can be overridden. This class accepts a TableIdentifier as its input parameter, and returns the class name in a String. Thus, to remove the tb_ from the class, we could do something like this:

public String tableToClassName(TableIdentifier tableIdentifier) {
  String packageName = "us.mikedesjardins.data";
  String className = super.tableToClassName(tableIdentifier);
  if (className.startsWith("Tb")) {
    className = className.substring(2);
  }
  return className;
}

Compile your CustomReverseEngineeringStrategy class, make sure it’s on the hibernatetools task definition’s classpath, and re-run the task. Voila! The Tb’s are gone!

There’s a similar method for column names named columnToPropertyName that handles naming your persisted class’s member variables. In my current project, the primary key columns are named the same as the table name, with “_id” appended to the end. However, in the object model, we like to just name the primary key property “id” to simplify the creation of generic DAOs. I use the columnToPropertyName method for this.

2.) Next Problem – The Generated POJOs need tweaking.
Perhaps the code that Hibernate generates is not to your liking. Maybe you work with standards-compliance-nazis who want a copyright header at the top of each class file. Or maybe all of your persisted objects implement the same interface for use with generic DAOs.

Under the covers, Hibernate uses freemarker to generate POJOs and mapping files. The templates that it uses can be edited to your liking, but doing it is not very straightforward.

First, unzip the hibernate tools jar into a temporary directory. Once unzipped, you’ll find a directory named pojo. This directory contains all of the templates used for POJO generation. Copy that directory into your build area and name it something like hib_templates.

The hbm2java task is actually just an alias for another hibernate tools target called hbmtemplate. With hbmtemplate, you can configure the location of the source templates. So we’ll remove the hbm2java task from the gen_hibernate target, and replace it with an equivalent hbmtemplate task:

<target name="gen_hibernate" depends="compile">
  <delete dir="${genhbm}" />
  <mkdir dir="${genhbm}" />
  <hibernatetool>
    <jdbcconfiguration
        configurationfile="${src}/hibernate-connection.cfg.xml"
        revengfile="hibernate.reveng.xml"
        packagename="us.mikedesjardins.data"
        detectmanytomany="true"
        detectoptimisticlock="true"
        reversestrategy="us.mikedesjardins.hibernate.CustomReverseEngineeringStrategy"/>
     <hbm2hbmxml destdir="${genhbm}" />
     <hbmtemplate templateprefix="pojo/"
              destdir="${genhbm}"
              template="hib_templates/Pojo.ftl"
              filepattern="{package-name}/{class-name}.java">
       <property key="jdk5" value="true">
       <property key="ejb3" value="false">
    </hbmtemplate>
  </hibernatetool>
</target>

Now that we’ve told hibernate tools where our template lives, we can edit it to our liking. For example, if we want to add a copyright notice to the top of each generated POJO, we could do so thusly:

//
// Copyright 2008 Big Amalgamated Mega Global Software Corp.  All Rights Reserved.
//
${pojo.getPackageDeclaration()}
// Generated ${date} by Hibernate Tools ${version}
 
<#assign classbody>
<#include "PojoTypeDeclaration.ftl"/>; {
.
.
.
(etc)

Now that we’ve done this, we can create more templates that, e.g., generate empty DAO classes, automatically create derived classes, etc.

Hope that helps!

Photo Credit: geishaboy500

Tags: , , ,
Posted in blog | 1 Comment »


Twenty minutes with Hibernate Search; A Cheesy Example

In a recent post, I showed a trick for determining which users in a system are running a long-running query. One commenter suggested that using a full-text search system made a lot of sense in those situations, and I wholeheartedly agree. So I decided to devote a new post to Hibernate Search!

In this example, I provide a live, online interactive search of an online cheese database, and you can download the example (more on that at the end of the post).

Step One – The Test Data
I spent the most time on this project was creating test data for the example program. I headed over to Freebase to see what was available for data sets. Among lots of other things, they have a free database of cheese. First, I downloaded the data in TSV format, dumped it into a raw table, and massaged it into a relational, normalized schema. I ended up with this when I was done:
So, a CHEESE can have only one ORIGIN (a country or region where the cheese is made), is made from one-to-many types of MILK, and may have zero-to-many TEXTURES associated with it.

Step Two – Build the Domain Model
The domain model for this system is very simple. Each Cheese class has an Origin, and a set of Milk and Textures:

@Entity @Table(name="MILK")
public class Milk {
  @Id @Column(name="milk_id")
  private Integer id;
 
  @Basic @Column(name="name")
  private String name;
 
  @Version @Column(name="version")
  private Integer version;
 
// Accessors omitted
}

@Entity @Table(name="ORIGIN")
public class Origin {
  @Id @Column(name="origin_id")
  private Integer id;
 
  @Basic @Column(name="name")
  private String name;
 
  @Version @Column(name="version")
  private Integer version;
 
// Accessors omitted
}

@Entity @Table(name="TEXTURE")
public class Texture {
  @Id @Column(name="texture_id")
  private Integer id;
 
  @Basic @Column(name="description")
  private String description;
 
  @Version @Column(name="version")
  private Integer version;
 
// Accessors omitted
}

@Entity @Table(name="CHEESE")
public class Cheese {
  @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
  @Column(name="cheese_id",nullable=false,unique=true)
  private Integer id;
 
  @Basic @Column(name="name")
  private String name;
 
  @ManyToOne(cascade={CascadeType.ALL})
  @JoinColumn(name="origin_id",nullable=false)
  private Origin origin;
 
  @ManyToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE})
  @JoinTable(name="CHEESE_MILK_MAP",
             joinColumns=@JoinColumn(name="cheese_id"),
             inverseJoinColumns=@JoinColumn(name="milk_id"))
  private Set<milk> milks = new HashSet<milk>();
 
  @ManyToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE})
  @JoinTable(name="CHEESE_TEXTURE_MAP",
             joinColumns=@JoinColumn(name="cheese_id"),
             inverseJoinColumns=@JoinColumn(name="texture_id"))
  private Set<texture> textures = new HashSet<texture>();
 
  @Version @Column(name="version")
  private Integer version;
 
// Accessors omitted
}

Step Three – Add Search Annotations and Configure Lucene
Behind the scenes, Hibernate Search uses the Apache Lucene search engine to do its indexing. In short, it maintains a mapping of object IDs to search terms in an external file, and updates the file when objects are added, updated, or deleted. To start using Hibernate Search, you’ll need to configure the location of these index files, as well as a search directory provider (we’ll just use the default). This is done in your Hibernate properties file, or (if you use JPA, like me), in persistence.xml:

<property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider" />
<property name="hibernate.search.default.indexBase" value="/var/lucene/cheese-indexes" />

Next, you need to indicate to Lucene which classes need to be indexed. You also need to indicate which data fields 1.) contain the document ID, and 2.) contain relevant search text. In our example, we only need to index the Cheese objects. We want to allow users to search on cheese name, milk name, origin, and texture.

First, we indicate that we want to index the Cheese objects by applying the @Indexed annotation to the class, and we elect to use the Id field to identify the Cheese objects to Lucene by applying a @DocumentId annotation to it. Next, we indicate that the cheese name contains searchable text by adding the @Field(index=Index.TOKENIZED, store=Store.NO) annotation to it. The annotation parameters are informing Hibernate Search to use Lucene’s default tokenizer to summarize the text, and not to store a copy of the document content.

We also want to allow users to search on Origin, Milk Name, and Texture. This text is not contained within the Cheese object, instead they’re in related object. So we need to add the @IndexEmbedded annotation to the member variables in the Cheese class that refer to the objects which contain the searchable text, and we also need to add the @Field(index=Index.TOKENIZED, store=Store.NO) annotation to the Milk, Origin, and Texture classes to indicate which fields are searchable.

When you’re done, the modified domain classes will look like this:

@Entity @Table(name="MILK")
public class Milk {
  @Id @Column(name="milk_id")
  private Integer id;
 
  @Field(index=Index.TOKENIZED)
  @Basic @Column(name="name")
  private String name;
 
  @Version @Column(name="version")
  private Integer version;
 
// Accessors omitted
}

@Entity @Table(name="ORIGIN")
public class Origin {
  @Id @Column(name="origin_id")
  private Integer id;
 
  @Field(index=Index.TOKENIZED)
  @Basic @Column(name="name")
  private String name;
 
  @Version @Column(name="version")
  private Integer version;
 
// Accessors omitted
}

@Entity @Table(name="TEXTURE")
public class Texture {
  @Id @Column(name="texture_id")
  private Integer id;
 
  @Field(index=Index.TOKENIZED)
  @Basic @Column(name="description")
  private String description;
 
  @Version @Column(name="version")
  private Integer version;
 
// Accessors omitted
}

@Indexed
@Entity @Table(name="CHEESE")
public class Cheese {
  @DocumentId
  @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
  @Column(name="cheese_id",nullable=false,unique=true)
  private Integer id;
 
  @Basic @Column(name="name")
  @Field(index=Index.TOKENIZED, store=Store.NO)
  private String name;
 
  @IndexedEmbedded
  @ManyToOne(cascade={CascadeType.ALL})
  @JoinColumn(name="origin_id",nullable=false)
  private Origin origin;
 
  @IndexedEmbedded
  @ManyToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE})
  @JoinTable(name="CHEESE_MILK_MAP",
             joinColumns=@JoinColumn(name="cheese_id"),
             inverseJoinColumns=@JoinColumn(name="milk_id"))
  private Set<milk> milks = new HashSet<milk>();
 
  @IndexedEmbedded
  @ManyToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE})
  @JoinTable(name="CHEESE_TEXTURE_MAP",
             joinColumns=@JoinColumn(name="cheese_id"),
             inverseJoinColumns=@JoinColumn(name="texture_id"))
  private Set<texture> textures = new HashSet<texture>();
 
  @Version @Column(name="version")
  private Integer version;
 
// Accessors omitted
}

Step Four – The Servlets
In this example, I didn’t want to rely on any web frameworks or even on JSPs, so I wrote a good old-fashioned servlet to exercise the search function. No sane person would ever do it this way. There are actually two servlets in the example – one for application initialization and one for the page itself.

The initialization servlet does the work of indexing all of the database data the first time through. For our small data set, this takes less than a minute. For larger data sets, it wouldn’t make sense to re-index everything every time you start the application. The initialization code iterates over all of the Cheese objects ant tells the FullTextEntityManger to index it:

public void init() {
  Dao<cheese> dao = new CheeseDao();
  EntityManager em = dao.getEntityManager();
  FullTextEntityManager fullTextEntityManager = Search.createFullTextEntityManager(em);
 
  List<cheese> cheeses = em.createQuery("select c from Cheese as c").getResultList();
  for (Cheese cheese : cheeses) {
    fullTextEntityManager.index(cheese);
  }
}

The main page servlet has some code in the doPost method to perform the search based on the contents of the text form field. That code looks like this (I’ve shortened it up a bit here by removing some error checking and HTML output):

public void doPost(HttpServletRequest request,
                   HttpServletResponse response) throws ServletException, IOException {
  response.setContentType("text/html");
  PrintWriter out = response.getWriter();
  emitHeader(out);
 
  String searchTerm = request.getParameter("searchterm");
  EntityManager em = dao.getEntityManager();
 
  FullTextEntityManager fullTextEntityManager =
    org.hibernate.search.jpa.Search.createFullTextEntityManager(em);
  MultiFieldQueryParser parser =
    new MultiFieldQueryParser( new String[]{"name",
                                            "origin.name",
                                            "milks.name",
                                            "textures.description"},
                               new StandardAnalyzer());
 
  try {
    org.apache.lucene.search.Query query = parser.parse(searchTerm);
    javax.persistence.Query hibQuery =
      fullTextEntityManager.createFullTextQuery(query,Cheese.class);
    List<cheese> result = hibQuery.getResultList();
    emitTable(out,result);
  } catch (ParseException e) {
    log.error("Got a parse exception", e);
    throw new ServletException(e.getMessage());
  }
  emitFooter(out);
  out.close();
}

That’s all there is to it! As you can see, setting up Hibernate Search is very simple. Most of the effort for this project was spent creating the data and making the servlets work.

Enjoy the Finished Product
To see this less-than-world-changing application in action, visit it here. You can also download the whole eclipse project and try it out for yourself. It comes with SQL dumps suitable for MySQL and PostgreSQL, and it has been tested with both environments under Tomcat 5.5.

Photo Credit: Chris Buecheler

Tags: , , ,
Posted in blog | 1 Comment »


Use Hibernate’s Custom Loaders to fake an aggregation view

Your Problem
You have a data model with table that contains data you want to aggregate. For instance (returning to my venerable Pizza Shop example), let’s say you have a PRODUCT table that enumerates the items your pizza shops sells, and a LOCATION table that contains all of your retail locations:


We also have a table that contains the sum of all of the previous days’ sales, broken down by PRODUCT and LOCATION:

This is all well and good, but we’ve been asked to create an Executive Dashboard for the President of the pizza chain, and she would like to see daily sales by product. She is not interested in a breakdown by location.

We could tally it up client side…
What if we just loaded the entire table into the client, and iterated over the per-product results, and present that? Unfortunately, ORM libraries are pretty stupid in situations like these, and will generate all kinds of expensive reads to the database when you try to solve the problem this way. If you want to learn more about lazy loading, and why you shouldn’t iterate over a collection, check out my earlier post here.

We could just create a view in the Database…
We could just create a view, and aggregate the data there. Then we can easily create a Hibernate mapping to that view. The query for the view is simple:

CREATE VIEW sales_by_product_view AS
SELECT product_id, sum(total_sales) AS total_sales
  FROM YESTERDAY_SALES
 GROUP BY product_id

However, we are working with a tyrannical DBA. She is not keen on proliferating views throughout our otherwise pristine schema every time the President has decided that the company needs a new widget for the executive dashboard application.

…or we could fake it with a custom loader
Instead, let’s create a Hibernate mapping that generates the same results as the view. First, let’s create a simple POJO to contain the results:

package us.mikedesjardins;
public class SalesByProduct {
  private Integer id;
  private Integer productId;
  private BigDecimal sales;
// accessors omitted
}

The corresponding mapping file would look like this. I re-used the product_id for the ID in this example. Note the loader element:

<hibernate-mapping package="us.mikedesjardins">
  <class name="SalesByProduct"
          dynamic-insert="false"
          dynamic-update="false"
          mutable="false">
    <id name="id" type="int" unsaved-value="null">
      <column name="__id" sql-type="int identity" not-null="true" unique="true" />
    </id>
    <property name="productId" type="int">
      <column name="__product_id" not-null="false" />
    </property>
    <property name="sales" type="java.math.BigDecimal">
      <column name="__total_sales" not-null="false" />
    </property>
    <loader query-ref="salesByProductQuery" />
  </class>
  <sql-query name="salesByProductQuery">
    <return class="SalesByProduct" />
    <![CDATA[
    select product_id as __id
         , product_id as __product_id
         , sum(total_sales) as __total_sales
      from YESTERDAY_SALES
     where product_id = :product_id
    group by product_id
   ]]>
  </sql-query>
</hibernate-mapping>

Note that you need to put a positional argument in the query, or Hibernate will get nasty about parsing it. Hope that helps!

Tags: , , ,
Posted in blog | 1 Comment »