Archive for the ‘java’ Category

Use Hibernate Validators to find Nasty DataTruncation exceptions

Tuesday, June 10th, 2008

If you develop Hibernate applications against either Sybase or MS SQL Server databases, you may have had the pleasure of seeing this little gem:

09:13:22,155 WARN [] org.hibernate.util.JDBCExceptionReporter – SQL Error: 0, SQLState: 22001
09:13:22,155 ERROR [] org.hibernate.util.JDBCExceptionReporter – Data truncation
09:13:22,155 WARN [] org.hibernate.util.JDBCExceptionReporter – SQL Error: 8152, SQLState: 22001
09:13:22,155 ERROR [] org.hibernate.util.JDBCExceptionReporter – String or binary data would be truncated.
09:13:22,159 ERROR [] org.hibernate.event.def.AbstractFlushingEventListener – Could not synchronize database state with session
.
.
.
Caused by: java.sql.DataTruncation: Data truncation

The most maddening thing about these errors is that the database won’t tell you which column is being truncated. A likely scenario is that you’ve developed a Web or Swing UI that permits the user to enter a value for a String field which is ultimately persisted to a database column, and the UI is not restricting the maximum length of the user’s input to the size of that the column supports. The question is, which field is the offender?

Hibernate Validators to the Rescue!
Situations like this are why it’s a pretty good idea to validate your data at the domain layer of your project, and not rely on your database and presentation layer to do all of the validation. Fortunately, Hibernate makes this pretty painless with validator annotations. Just import the org.hibernate.validator.Length annotation, and add the @Length annotation in your entity class, e.g.:

@Column(name="description",length=20)
@Length(max=20)
private String description;

Note that putting the length attribute in the @Column annotation is not enough. That attribute is used for generating DDL, but will not cause any validation to take place.

Now that our new Annotation is in place, we get the slightly more useful stack-trace below:

org.hibernate.validator.InvalidStateException: validation failed for: us.mikedesjardins.data.persist.MyClass
at org.hibernate.validator.event.ValidateEventListener.validate(ValidateEventListener.java:148)
at org.hibernate.validator.event.ValidateEventListener.onPreInsert(ValidateEventListener.java:172)
at org.hibernate.action.EntityIdentityInsertAction.preInsert(EntityIdentityInsertAction.java:119)
.
.
.

Of course, you should catch these exceptions and do something less hostile with them. For now, we’ll just log the details and re-throw so you can get the general gist of what you can do. Here’s a generic insert method in a DAO base class:

public void insert(T valueObject, Class... clazz) {
 try {
   Session s = getSession();
   s.save(valueObject);
   s.flush();
   s.refresh(valueObject);
 } catch (InvalidStateException v) {
   setToRollback();
   log.error("insert(), failed validation " + valueObject.toString(), v);
   InvalidValue[] invalid = v.getInvalidValues();
   for (int i=0; i<invalid.length; ++i) {
     InvalidValue bad = invalid[i];
     log.error("insert(), " + bad.getPropertyPath()
     + ":" + bad.getPropertyName()
     + ":" + bad.getMessage());
   }
   throw v;
 }
}

As you can see, the InvalidStateException contains an array of InvalidValue objects which describe the nature of the validation error. You can either log this information, or even present it to the user.

Hope that helps! Do you use Hibernate’s validator annotations for something nifty? Let us know in the comments!

The Hibernate Song

Friday, June 6th, 2008

(to the tune of The Spiderman Theme Song from the original cartoon series)

Hibernate, Hibernate
O/R Mapping sure is great
Gavin King, and H-Q-L
Make my life a living hell
Look out! Here comes Hibernate!

Mapping Files in Hibernate
Wish my team would annotate
Are its queries optimized?
Who knows – it’s always a surprise
Hey there, there goes Hibernate!

Don’t fetch data now
Lazy load, ‘cuz you might guess
I don’t need it now
Hibernate, you do know best!

Hibernate, Hibernate
Friendly neighborhood Hibernate
Grab the jars, and climb aboard
OO models are your reward
To it…
Relational DBs are old-school
When you need a complex tool
You need the Hibernate!

Amp’d Mobile, (Almost) One Year Later

Wednesday, May 14th, 2008

It’s been almost a year since Amp’d Mobile imploded. Amp’d Mobile was a Mobile Virtual Network Operator (MVNO). This meant that they were a wireless carrier without a network – instead, they relied on a “real” national carrier’s network (in Amp’d's case, Verizon’s) to deliver voice and data services. As one of the software architects who designed their central integration point and contributed to their billing platform, I had a unique view into it’s rise and abrupt demise. I’ve wanted to write a post about my experience for quite some time now, and I think that enough time has passed that I can safely speak my mind – both companies are more-or-less defunct at this point.

I should note that all of these opinions are my own – I don’t speak on behalf of BCGI, Amp’d Mobile, or anybody else.

My Role
Amp’d Mobile contracted with my former employer, Boston Communications Group (usually known by its acronym, BCGI). Among other things, BCGI sold a Postpaid Billing platform called Voyager. Up until Amp’d Mobile, Voyager’s primary market was small, regional wireless carriers with fewer than 100,000 subscribers. Before Amp’d, the Voyager product didn’t contribute much to BCGI’s bottom line and served more as a product portfolio completer than as a real potential for revenue. BCGI’s main focus had instead been on Prepaid Wireless and Payment Services.

My role was primarily to design and create a web service API to Voyager, so that other components in Amp’d's back office could interact with the billing system. This not only included billing for voice and content, but also provisioning prepaid and postpaid services on Verizon’s network, accepting payments, and providing a customer care UI for Amp’d's call center. Additionally, I did some testing to gauge how well the system would scale once Amp’d added the half-million-or-so subscribers that they were projecting they’d have in their first year.

What Went Well
I really don’t intend for this post to be an airing of dirty laundry. Despite the implosion of both companies, there were a few things that went very well.

  • BCGI was a fairly stodgy company for its size, with a heritage in larger telecom and wireless companies like Verizon and EDS. Having said that, they gave a lot of latitude to the developers involved with this project. The pace of the development necessitated a more agile approach than BCGI was accustomed to, and the upper management at BCGI did a good job of trying to accommodate the furious speed at which things needed to be done. A lot of the normal paperwork and bureaucracy was pushed aside to get things done more quickly, to everyone’s benefit.
  • Some of the development work for the project was offshored, with mixed success. The projects that I’d consider successes related to components that did real-time provisioning of services with the carrier’s network elements. These projects were small, self-contained, with clear objectives that did require a lot of business domain knowledge.
  • With a few exceptions, the API scaled quite well. We quickly ramped up to support hundreds of thousands of transactions per day. These came handsets, web sites, payment platforms, etc.
  • Integration between our API and Amp’d's web components went very well. Developers from geographically dispersed areas used Instant Messaging quite extensively to communicate, and it was a far more productive means of communication than the old “our developer talks to our project manager who talks to their project manager who talks to their developer” scheme that seems so pervasive in larger companies.

What Didn’t Go Well
There wouldn’t be much to write about if there weren’t a few negatives in the postmortem, right? So here they are; first, the technical Challenges:

  • Scaling the batch billing processes were more difficult than I anticipated. When I did my initial testing, I used an existing carrier’s data, and multiplied their user data gradually to project how quickly we’d be able to rate call records with larger carriers. I didn’t count on the fact that my source carrier’s subscribers made far fewer calls per subscriber than Amp’d's subscribers eventually made. Amp’d's market demographic was much younger than the demographic for the source data that I used, and younger subscribers make more calls. In retrospect, this should have been obvious.
  • Another scaling difficulty was caused by the manner by which we received call detail records. Mediation is the process by which you obtain raw call records from a network device, and turn those records into a billable records in your billing system. For a traditional carrier, this process takes place daily (at an absolute minimum) or more often as necessary. In Amp’d's case, we received call records once per month. This meant that we needed to process and rate over 50 million call records in a matter of hours, which is no small feat. It’s especially difficult when you’re using a billing system that was designed to mediate only hundreds of thousands of records daily, and bill an order of magnitude fewer records once per month. We managed to scrape by, but there were some pretty severe growing pains.
  • We didn’t adequately anticipate the level of fraud that we encountered. We were required to make changes to head off fraud problems rather hurriedly, and that led to slapdash implementations that were prone to errors.
  • The customer care UI was buggy. I previously did an entire post just about that topic.

Of course, there were business-related challenges, too.

  • Most of the executive staff in both companies (Amp’d and BCGI) had a lot of experience with prepaid environments and mobile content, but very little experience in postpaid environments. This was particularly true when Amp’d Mobile was first getting started. By the time Amp’d brought on more seasoned management with post-paid experience, it was already too late. It’s important to note that the postpaid business was about three-quarters of Amp’d's subscriber base.
  • Because many of the key players came from a prepaid background, the enhancements that were requested for the platform often involved marketing content or prepaid services, instead off addressing fundamental business processes like billing and collections. BCGI, itself of prepaid pedigree, was happy to oblige. Thus, the limited development resources were allocated to content, marketing, and prepaid tasks instead of revenue assurance, payment, and other postpaid tasks. Sadly, Voyager had built-in collections modules that were never used by Amp’d because it was never a priority for them. If this had been made a priority in the very beginning, Amp’d might still be in business today.
  • People still buy handsets with the intention to talk on them, not to purchase media content. When Amp’d launched, their voice plans were not competitive, and the rate of adoption was low. I think the belief was that the content was a compelling enough reason to sell the phone. However, having a good plan for good old send-to-end voice calls is still critical to attract new subscribers. Once they adjusted their rate plans for this, net add rates improved significantly.

What I Learned
It’s easy to armchair-quarterback the Amp’d experience. I obviously have the benefit of hindsight. Here are some of the things that I took away from the experience:

  • As I wrote in an earlier post, business domain knowledge is key. If you’re entering a new business space, make sure you have an expert on your side who knows what the pitfalls will be, from the very beginning.
  • Building a national MVNO, particularly one that offers post-paid service, is a lot harder than it sounds. I think that a lot of people mistakenly believe that the only difficult part of building a nationwide wireless carrier is having the network. But building the operational staff to support billing, credit, collections, and customer care is a huge undertaking that takes years to get right at traditional carriers. These critical business processes aren’t cheap, either, even if you manage to outsource them. I’m not sure if an MVNO can be competitive given the cost of using the host network, plus the cost of the back-office processes.
  • Always plan for fraud. The bigger you get, the bigger target you become for fraud. Look at every process as a thief would – payment, distribution, service delivery, etc. Everywhere in your system where there is something of value is a target. Plan for it in advance.
  • Scalability tests might be less useful than you think they are. It’s hard to predict how things will scale, and which APIs or processes will take the hardest beating. It’s probably safe to just assume that your processes will need to adapt quickly to cope with increased volume, and to be prepared for it by designing your processes to scale horizontally wherever possible.
  • I had a blast. This project was one of the most rewarding experiences I’ve ever had as a developer, and it’ll be a long time before I can top it. I worked with some amazing people at Amp’d, BCGI, and a lot of Amp’d's other vendors. The brainstorming sessions in Amp’d's amphitheater with the whiteboard is the stuff that developer/architect types love. I still pine for the good old days when I felt like I was a part of something revolutionary in the wireless space.

Pizza Shop III : JPA Event Listeners

Monday, March 31st, 2008

This comment was recently posted to one of my blog entries a while back:

So, let’s say your data model has some tables with a columns that indicate the last person who touched a record, like Madhan’s example above. In most applications, the end-users of the database client share a common login to the database, and have individual logins which are specific to the application’s domain (i.e., you don’t have a database login mapped to each end user; maintaining this scheme would be a nightmare). For that reason, triggers can’t be used as a solution, because database triggers don’t know anything about which application end-user is responsible for making a data change.

The last thing you want to do is litter your codebase with snippets of code that set the username on your persisted objects manually; not only is it unnecessary duplication, but you’ll probably also end up missing cases where you should be setting the username.

EventListeners to the Rescue
Fortunately, JPA supports the notion of “EventListeners.” An event listener intercepts many of the API calls that modify a persisted object’s lifecycle, and thus may be used to inject business logic that needs to be duplicated over many different objects. AOP aficionados might refer to this as a cross-cutting “aspect” of the domain layer.

Return to the Pizza Shop
Readers of my blog (all three of them, including me) might recall my venerable Pizza Shop example. Here are the earlier Pizza Shop posts if you’d like to catch up: Part 1 and Part 2. I’m going to drag the Pizza Shop out again to demonstrate how to create an “audited” table, which shows the last user who modified a record. As always, the source is available here, and it’s been tested against MySQL, Postgres, and MS SQL Server, with Hibernate, OpenJPA, and TopLink.

This takes just five easy steps… four, really, ‘cuz the fourth step creates a mock object for testing, so it doesn’t really count!

Step One – New schema
Add a nullable column named username to the Order table… something like this should work if you have existing pizzashop schema for some reason:

ALTER TABLE ORDER ADD username VARCHAR(10)

Step Two – Create an Interface and Implement It
This step isn’t strictly necessary, but it’s probably safe to assume that we’ll want to add this functionality to other tables someday. The interface just adds accessor methods for the username:

public interface AuditedObject {
  public String getUsername();
  public void setUsername(String username);
}

Then, make the Order table implement AuditedObject. Add a member variable with a mapping to the username column to the Order table, and corresponding accessor methods:

public class Order implements IdObject, AuditedObject {
.
.  
  @Basic @Column(name="username")
  private String username;
.
.
  public String getUsername() {
      return username;
  }
  public void setUsername(String username) {
      this.username = username;
  }
.
.
}

Step Three – Add an EntityListener Annotation
This is the secret sauce. In the JPA Framework, EventListeners allow you to fire some trigger code when a lifecycle event occurs on a persisted object. You do this by associating your persisted class with an EventListener class. We’ll implement our EventListener class after we’ve added the following annotations:

@Entity @Table(name="PIZZA_ORDER")
@EntityListeners(AuditedEntityListener.class)
public class Order implements IdObject, AuditedObject {
.
.

Step Four – Write a Mock Object for the Username for Testing
In the real world, you’d probably stuff the username into the servlet context object. For our simple tests, we’ll need to mock up an object to maintain a username for the duration of our tests. It can be something stupidly simple, like this:

public class MockContext {
  private static String username;
 
  public static String getUsername() {
      return username;
  }
  public static void setUsername(String username) {
      MockContext.username = username;
  }
}

We’ll set the username at the start of our tests, and read the username from our MockContext from the EventListener.

Step Five – Write the EntityListener class
JPA allows you to inercept method calls using seven annotations:

  • @PrePersist and @PostPersist are called before and after an object is persisted.
  • @PreUpdate and @PostUpdate are called before and after synchronization with the database. @PreRemove and @PostRemove are called before and after an object is removed from the persistent state.
  • @PostLoad is invoked immediately after an object is loaded from the database.

In our case, we’re going to populate the username instance variable when the object is persisted, so we will want to create an EntityListener class with the @PrePersist annotation. The method’s signature takes an Object parameter which is the object getting updated, and returns void. The class will look something like this:

public class AuditedEntityListener {
  @PrePersist
  public void updateUser(Object o) {
      if (o instanceof AuditedObject) {
        String username = MockContext.getUsername();
        ((AuditedObject)o).setUsername(username);
      }
  }
}

Voila! When you run the tests, the username fields are populated and persisted!

Audit M&M’s Photo Courtesy Joe Hall.

Using JavaMail to read and extract attachments

Thursday, March 13th, 2008

Recently I had to create a JavaMail-based e-mail client that polled an IMAP server for incoming e-mail messages. The client needed to read each message, find any attachments, and save those attachments to a directory on the application server. I had never had a need to do this with the JavaMail API before, so I did the first thing that many developers do when they’re venturing into unknown territory, and Google’d for an example. I found lots of examples of sending attachments, but not many of receiving them. The few examples that I did find of receiving mail with the JavaMail API were too simple. So I had to figure out how to do it by (gasp) reading the documentation. Fortunately, it wasn’t very difficult, but I thought I’d share it for the next programmer that turns to Google for a solution.

In my application, I used the Quartz library to periodically call a method named receive, which does the grunt work of pulling stuff off the server. This code is similar to other examples you might find out there Google’ing, and it’s pretty mundane stuff. The MailReceiveException is a catch-all Exception class that I wrote:

public Message[] receive(String server, String user, 
                         String password, String directory) 
                 throws MailReceiveException {
  Message[] msgs;
  Store store = null;
  Folder folder = null;
  try {
    store = session.getStore("imap");
    store.connect(server, user, password);
    folder = store.getDefaultFolder();
    if (folder == null) {
      throw new MailReceiveException("No default folder");
    }
    folder = folder.getFolder("INBOX");
    if (folder == null) {
      throw new MailReceiveException("No IMAP INBOX");
    }
    folder.open(Folder.READ_WRITE);
    msgs = folder.getMessages();
    for (int i=0; i<msgs.length; ++i) {
      Message msg = msgs[i];
      // don't fetch messages that we've already processed
      if (!msg.isSet(Flags.Flag.SEEN)) {
        String from = "unknown";
        if (msg.getReplyTo().length >= 1) {
          from = msg.getReplyTo()[0].toString();
        } else if (msg.getFrom().length >= 1) {
          from = msg.getFrom()[0].toString();
        }
        String subject = msg.getSubject();
        String filename = directory + "/" + from + ": " + subject;
        // This is where the real work will get done.
        this.saveParts(msg.getContent(), filename);
        msg.setFlag(Flags.Flag.SEEN,true);
      } else {
        // Delete anything that is more than sixty days old.
        Date receivedOn = msg.getReceivedDate();
        GregorianCalendar cal = new GregorianCalendar();
        cal.add(GregorianCalendar.DATE,-60);
        if (receivedOn.before(cal.getTime())) {
          msg.setFlag(Flags.Flag.DELETED, true);
        }
      }
    }
 
    // Rats' nest of catch blocks omitted... make sure you close 
    // the folder and the mail store in a finally block, as well 
    // as expunge any messages that have been marked for deletion.
 
  }
  return msgs;
}

There’s one small detail that I had to learn the hard way, and that is that the “Parts” within Multipart MIME messages can themselves contain other Parts. So you have a nifty opportunity to use some of that fancy recursion stuff that you learned about in your “Intro to Programming” course!

public void saveParts(Object content, 
                       String filename) 
             throws MailReceiveException {
  OutputStream out = null;
  InputStream in = null;
  try {
    if (content instanceof Multipart) {
      Multipart multi = ((Multipart)content);
      int parts = multi.getCount();
      for (int j=0; j<parts; ++j) {
        MimeBodyPart part = (MimeBodyPart)multi.getBodyPart(j);
        if (part.getContent() instanceof Multipart) {
          // part-within-a-part, do some recursion...
          this.saveParts(part.getContent(), filename);
        } else {
          String type = part.getContentType();
          String extension = "";
          if (part.isMimeType("text/html")) {
            extension = "html";
          } else {
            if (part.isMimeType("text/plain")) {
              extension = "txt";
            } else {
              // Try to make a reasonable guess about the  
              // extension from the MIME type.
              int end = type.indexOf(';');
              if (end < 0) {
                end = type.length();
              }
              extension = type.substring(type.indexOf('/')+1,end);
            }
            filename = filename + "." + extension;
            out = new FileOutputStream(new File(filename));
            in = part.getInputStream();
            int k;
            while ((k = in.read()) != -1) {
              out.write(k);
            }
          }
        }
      }
    }
  } catch (MessagingException e1) {
    log.error("Messaging Exception", e1);
    throw new MailReceiveException("Error fetching e-mail");
  } catch (IOException e2) {
    log.error("Caught IOException reading e-mail.", e2);
    throw new MailReceiveException("Error fetching e-mail");
  } finally {
    if (in != null) {
      try {
        in.close();
      } catch (IOException e6) {
        log.error("Unable to close input stream");
      }
    }
    if (out != null) {
      try {
        out.flush();
        out.close();
      } catch (IOException e7) {
        log.error("Unable to close output stream.");
      }
    }
  }
}

All of this work was done using JavaMail 1.3. I believe newer versions of the API have a savePart method, so you don’t need to write out the parts to a FileOutputStream byte-by-byte as I have above.

Enumerated Types with JPA, and your Sock Drawer

Monday, February 18th, 2008

One feature of JPA that didn’t exist in plain-vanilla Hibernate is support for Enumerated types. I haven’t seen a lot of examples of this in practice or on the internet, so in this post I’ll show one example of how to use JDK 5 enumerations with JPA.

For our example, we are going to create an inventory system for our sock drawer. It is comprised of only two tables. The first table, called SOCK, contains one row per sock in our drawer. The columns of the table are:

  • sock_id – an auto-incrementing identity column.
  • sock_description – a varchar column for a free-form text description of the sock.
  • sock_pattern_id – a reference to a row in the SOCK_PATTERN table.

As you may have guessed, the SOCK_PATTERN table looks like this:

  • sock_pattern_id – an integer primary key. It’s not auto-incrementing, because we will want to have control over the contents of the field.
  • sock_pattern_description – a varchar column for a free-form text description of the pattern.

We need to “prime” our SOCK_PATTERN table with the valid patterns and create a foreign key relationship between the two tables:

INSERT INTO SOCK_PATTERN (sock_pattern_id,sock_pattern_description) VALUES (0,'SOLID');
INSERT INTO SOCK_PATTERN (sock_pattern_id,sock_pattern_description) VALUES (1,'STRIPES');
INSERT INTO SOCK_PATTERN (sock_pattern_id,sock_pattern_description) VALUES (2,'POLKA_DOT');
INSERT INTO SOCK_PATTERN (sock_pattern_id,sock_pattern_description) VALUES (3,'ARGYLE');

Note that we populated sock_pattern_id starting with zero; this is important because the Enumeration below is zero-indexed.

Next, let’s create the classes:

public enum SockPattern {
 SOLID, STRIPES, POLKA_DOT, ARGYLE
}
 
@Entity @Table(name="SOCK")
public class Sock {
 @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
 @Column(name="sock_id")
 private Integer id;
 
 @Column(name="sock_description")
 private String description;
 
 @Enumerated @Column(name="sock_pattern_id")
 private SockPattern pattern;
 
 public Integer getId() { return id; }
 public void setId(Integer id) { this.id = id; }
 
 public String getDescription() { return description; }
 public void setDescription(String description) { this.description = description; }
 
 public SockPattern getPattern() { return pattern; }
 public void setPattern(SockPattern pattern) { this.pattern = pattern; }
}

Now, to use our wonderful contraption, you’d do something like the following:

public class Demo {
 private static EntityManagerFactory emf;
 static {
     Demo.emf = Persistence.createEntityManagerFactory("sockdrawer");
 }
 
 @Test
 public void socksOne() {
     Sock sock = new Sock();
     sock.setDescription("My favorite sock.");
     sock.setPattern(SockPattern.ARGYLE);
     EntityManager em = Demo.emf.createEntityManager();
     em.getTransaction().begin();
     em.persist(sock);
     em.getTransaction().commit();
     em.close();
 }
}

There are some obvious pitfalls to this approach. The object/relational mapping is very brittle; for this code to work, the IDs in the database always need to match the values that the ORM tool gets from the enumeration. Changing these values at a later date could cause some surprising results, and you can’t insert new rows without updating the Enumeration and recompiling. It does obviate the need to map the SOCK_PATTERN table, and you won’t need to worry about the details of cascading the persistent state of related Sock and SockPattern objects.

It’s just a new tool in the JPA toolbox.

(The code in this blog post was tested w/ Postgres and Toplink)

A Delicious and Simple JPA Mapping tutorial: The Pizza Shop

Thursday, January 31st, 2008

So, another JPA tutorial. What makes this one different? Well, for one thing, this one comes with a working, downloadable project that works with Eclipse, NetBeans, and IntelliJ IDEA 7. It’s packaged with Hibernate, Toplink, and OpenJPA. And it’s been tested with MySQL, PostgreSQL, MS SQL Server, and Sybase. In other words, it works with 36 different IDE/JPA Provider/Database combinations!

Another thing that makes this tutorial different is the subject matter: Pizza! Who doesn’t love pizza? Except lactose intolerant people. And people who can’t eat gluten. But other than them, who doesn’t love pizza? So we’re going to create a simple database model for a pizza shop’s point-of-sale system.

The Schema
Unsurprisingly, the starting point for any ORM task is usually the database schema (there are people who start with the Objects and work “backward” to the schema, but I haven’t worked with any of them yet). In our example, we have a pristine, consistent, completely normalized schema. In other words, it’s probably nothing like you’ll ever be lucky enough to see in the real world! Here’s our simple little ERD:From this ERD, we can infer the following: 1.) An order is comprised of zero or more pizzas. 2.) A pizza is associated with one size. 3.) A pizza may be associated with a string of text containing “special instructions.” 4.) A pizza may have zero or more toppings. You probably also notice that each table has a column called version. This will be used for an optimistic locking strategy.

The first question is, “Where should we start?” There’s no right answer for this, but I find that it’s easiest to start working with the entities with the fewest dependencies. For example, you can’t have an order without a pizza, and you can’t have a pizza without a size, so maybe it makes sense to start with the size. But before that, we’ll want an ID interface.

An ID Interface
First things first. You’ll notice that, in our schema, every table has an integer ID. It’s often a good idea to have all of your objects implement the same interface for accessing the ID, because it makes it easier to create Generic DAOs (more about that in a future post). For now, let’s make a really simple interface like this:

public interface IdObject {
  public void setId(Integer id);
  public Integer getId();
}

Many-to-One Unidirectional Relationships
Now that we’ve gotten that out of the way, let’s create the Size class. It’s a simple POJO littered with annotations, like this:

@Entity
@Table(name="PIZZA_SIZE")
public class Size implements IdObject {
  @Id
  @Column(name="pizza_size_id")
  private Integer id;
 
  @Column(name="pizza_size_description")
  private String description;
 
  @Column(name="pizza_size_base_price")
  private BigDecimal basePrice;
 
  @Version @Column(name="version")
  private Integer version;
 
  public Integer getId() { return id; }
  public void setId(Integer id) { this.id = id; }
 
  public String getDescription() { return description; }
  public void setDescription(String description) { this.description = description; }
 
  public BigDecimal getBasePrice() { return basePrice; }
  public void setBasePrice(BigDecimal basePrice) { this.basePrice = basePrice; }
 
  public Integer getVersion() { return version; }
  public void setVersion(Integer version) { this.version = version; }
}

Here are some notes on the annotations that were used in the above class:
@Entity
tells the JPA provider that this is a managed object.
@Table specifies the table name. The JPA provider will attempt to default the table name to a sane value based on the class name, but I like to be explicit. I’m funny that way. Perhaps it’s OCD. Or a power-trip.
@Column indicates the name of the column, and can include other attributes about the column (you’ll see a few additional attributes later on). Again, JPA can try to default this to sane values for you, but I like to be explicit.
@Version indicates that a particular column is used for indicating when a row is updated. This column can then be used in an optimistic locking scheme.

Next, let’s do the Pizza object to show how we map a Pizza to a Size.
One thing I that always tripped me up when I started out with ORM
tools was the difference between “One-to-Many” and “Many-to-One.” I never knew, if I call a relationship many-to-one in my metadata, is this object the one that there are many of, or is it the other way around? The answer is that “this” object always comes first. ManyToOne means that “there are many of this object to one of those objects.” The “Many” side is often the side that has the foreign key.

In our case, there will be many Pizzas that are the same size. So when we make our Pizza object, we will want to use the @ManyToOne annotation. Here’s what the Pizza object looks like so far. I’ve omitted the getters and setters to save space:

@Entity
@Table(name="PIZZA")
public class Pizza implements IdObject {
  @Id
  @Column(name="pizza_id")
  private Integer id;
 
  @ManyToOne(cascade={CascadeType.ALL})
  @JoinColumn(name="pizza_size_id",nullable=false)
  private Size size;
 
  @Version @Column(name="version")
  private Integer version;
 
  // (Accessor methods omitted)
}

Note that, when we made our Size object, we did not include a reference to the Pizza. That was an intentional design decision. In this application, it’s unlikely that we will want to instantiate a Size object, and get a collection containing all of the Pizzas of that size, so we don’t bother with mapping it. This is called unidirectional association.

The @ManyToOne annotation specifies a cascade attribute. There are several different settings for this attribute, which you can read more about here. I tend to cascade the persistent state to all related objects because it reduces the amount of redundant API calls. By default, JPA does not cascade pers istence to related objects. I’ll cover the cascade attribute in future posts, but for now, we’ll go with my personal preference, because I’m writing the article!

The @JoinColumn annotation indicates the column name that defines the linkage between the Pizza and the Size. You’ll also note that we’ve included some additional attributes on our @Column and @JoinColumn annotations. The unique and nullable attributes are particularly useful if you use tools to generate schema DDL from your mappings.

One-to-Many bidirectional relationships
Both the SpecialInstruction and the Order objects are examples of One-to-Many bidirectional relationships. In the case of SpecialInstruction, it is likely that we will care about which Pizza an instruction is associated with, and likewise for the Order. A bidirectional one-to-many relationship implies that one object has a collection of other o bjects. For example, an Order has a collection of Pizzas. First, lets add an order attribute to our Pizza object:

@Entity
@Table(name="PIZZA")
public class Pizza implements IdObject {
.
.
  @ManyToOne(cascade={CascadeType.ALL})
  @JoinColumn(name="pizza_order_id",nullable=false)
  private Order order;
 
  public Order getOrder() { return order; }
  public void setOrder(Order order) { this.order = order; }
.
.
}

Next, let’s create an Order object to contain our collection of Pizzas, like this:

@Entity
@Table(name="PIZZA_ORDER")
public class Order implements IdObject {
  @Id
  @Column(name="pizza_order_id")
  private Integer id;
 
  @OneToMany(cascade={CascadeType.ALL},mappedBy="order")
  private Set pizzas = new HashSet();
 
  @Version @Column(name="version")
  private Integer version;
 
  // (version and id accessors omitted)
 
  public Set getPizzas() {  return pizzas; }
  public void setPizzas(Set pizzas) { this.pizzas = pizzas; }
  public void addPizza(Pizza pizza) {  pizza.setOrder(this);  this.pizzas.add(pizza); }
}

There are a couple of things you worth noting about this mapping:
1.) The @OneToMany annotation uses the mappedBy attribute to indicate which member of the related object defines the linkage between the two tables. In this case, we are saying that the Pizza object contains a member named order, which defines the linkage between the two objects.
2.) I’ve created a utility method called addPizza. This simplifies setting both sides of the bidirectional relationship by setting the Order object on the Pizza and adding the Pizza to the Order’s collection. Users of this class will only need to make one method call to do both.

Many-to-Many relationships via a Join Table
The last thing we’ll cover is how to map Join Tables. In our ERD, you can see that Toppings are modeled in the database with a TOPPING table that contains all of the valid toppings, a PIZZA table that contains all of the valid Pizzas, and a PIZZA_TOPPING table in the middle that maps all of the valid Pizzas to all of the valid Toppings. You could create an object called PizzaTopping that corresponds to the PIZZA_TOPPING table. Then you could have a One-to-Many relationship from the Pizza to the PizzaTopping, and a One-to-One from each PizzaTopping to a Topping. That would be very cumbersome to work with! Fortunately, there’s a better way.

Logically, a Pizza has a collection of Toppings. In our Java code, we really shouldn’t care about the fact that there is a PIZZA_TOPPING join table in the middle. First, let’s create a simple Topping class:

@Entity
@Table(name="TOPPING")
public class Topping implements IdObject {
  @Id
  @Column(name="topping_id")
  private Integer id;
 
  @Column(name="topping_description")
  private String description;
 
  @Column(name="topping_price")
  private BigDecimal price;
 
  @Version @Column(name="version")
  private Integer version;
 
  // (Accessors omitted)
}

This is how the association would be mapped in the Pizza class:

@Entity
@Table(name="PIZZA")
public class Pizza implements IdObject {
.
.
  @ManyToMany(cascade={CascadeType.ALL})
  @JoinTable(name="PIZZA_TOPPING",
             joinColumns=@JoinColumn(name="pizza_id"),
             inverseJoinColumns=@JoinColumn(name="topping_id"))
  private Set toppings = new HashSet();
 
  public Set getToppings() { return toppings; }
  public void setToppings(Set toppings) { this.toppings = toppings; }
  public void addTopping(Topping topping) { this.toppings.add(topping); }
.
.
}

The @JoinTable annotation defines three key attributes:
name: Identifies the name of the join table.
joinColumns: This attribute identifies the column name in the join table that points to this object.
inverseJoinColumns: This attribute defines the column name in the join table that points to the other objects.

From your Java code, the semantics for dealing with toppings on a pizza are just like any other set, much like you’d work with a One-to-many object.

Conclusion
Since the introduction of annotations, object/relational mapping is one of the easiest aspects of working with JPA over other frameworks. You can see this whole project in action on your IDE of choice by downloading it here (or from here if that doesn’t work for some reason). It should be a pretty reasonable starting point if you want a reference project to start playing with JPA. I hope to revisit the Pizza Shop project to cover other JPA topics in future posts.

Glassfish to Tomcat w/ JDBC Connection Pooling

Monday, January 21st, 2008

Here’s your don’t-waste-hours-on-something-silly-that-I-did tip for the day.

I recently had the opportunity to change an existing in-house web application from running under Tomcat 5.5, to Glassfish v2. The application uses JDBC connection pooling in the application server, and uses JNDI to find the pool. The only stumbling block I encountered was that, immediately after deploying the application, Hibernate would always complain that it couldn’t find the connection:

org.hibernate.HibernateException: Could not find datasource
at org.hibernate.connection.DatasourceConnectionProvider.configure(DatasourceConnectionProvider.java:56)
at org.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:124)
at
.
.
(yadda yadda)
.
Caused by: javax.naming.NameNotFoundException: No object bound to name java:/comp/env/jdbc/MyDB
.
.

As it turns out, the datasource that I was using in hibernate.cfg.xml was perfectly fine for Tomcat, but not for Glassfish. I had put the fully qualified JNDI file in my config file:

<property name="connection.datasource">java:/comp/env/jdbc/ApptrackDB</property>

Glassfish doesn’t like that. Once I changed it to:

<property name="connection.datasource">jdbc/ApptrackDB</property>

Everything worked swimmingly.

Build Date on a Tapestry 4 login page using Ant

Thursday, December 13th, 2007

Recently, I had to put a build timestamp onto a login page for a web application I’m developing at work. The web application is written using Tapestry 4.1, but some of the techniques are equally applicable to other frameworks. I thought I’d share.

First, you need to setup your ant task to grab a timestamp, and put it into your manifest file. You do so using the tstamp task, like this:

The tstamp task is taking the current date and time, formatting it as specified by pattern (just as you’d specify it in a SimpleDateFormat) and placing it in the buildtstamp variable. The manifest task builds a MANIFEST.MF file which ends up in your deployed web application’s META-INF directory. You’ll notice that I’m also putting the name of the user who built the application into the manifest.

Next, we need to read the Manifest from our application. The first screen presented by my Tapestry app is LogOn.java. First, use HiveMind to inject the ServletContext into my page:

@InjectObject(“service:tapestry.globals.ServletContext”)
public abstract ServletContext getServletContext();

Also, we need to create an abstract method into which we’ll store and retrieve the build date:

public abstract String getBuiltOn();
public abstract void setBuiltOn(String builtOn);

Finally, we need to read the Manifest file in our pageBeginRender method, and set the “Built On” date accordingly. This is how I did this:

public void pageBeginRender(PageEvent event) {ServletContext sc = this.getServletContext();String filename = sc.getRealPath("/META-INF/MANIFEST.MF");try {  BufferedInputStream i = new BufferedInputStream(new FileInputStream(filename));  Manifest m = new Manifest(i);  Attributes attrib = m.getMainAttributes();  this.setBuiltOn(attrib.getValue("Build-Date"));} catch (Exception e) {  log.warn("Unable to read MANIFEST.MF");}}

Finally, we need to actually render this on the LogOn page. I did this with a simple Insert component directly on the html page:

Built: <span jwcid=”@Insert” value=”ognl:builtOn”>

And voila! You have a build date on your log page, which can come in handy, e.g., when your QA team doesn’t know which version they’re testing!


© 2012 Mike Desjardins. All Rights Reserved.