software development for the
existentially challenged
Older | Newer Older | Newer
Some quick messing around with JPA, Groovy, and HSQLdb
comment share
The more I look, the more I'm seeing JPA all over. I've been using TopLink for years in my corporate job. It works, but the interfaces are old and kind of weird. XML metadata is bad enough, but even programatic mapping isn't especially pretty, either. TopLink (and the open source EclipseLink) implements JPA, so I'll try to use it in my day job for new stuff.

JPA's use of annotations for mapping is so clean and nice, I can barely stand it. I've been working on a prototype for a simple virtual closet application, mostly. Working my way through it, I mapped up a bunch of Groovy objects and did some simple stuff in hsqldb.

The persistence model looks like this:

Users have Closets, which contain ClothingItems, which are individual realizations of the Clothing type. Clothing contains a reference to a Clothing (the type of clothing). Just about everything in the model is Taggable. There are very few data elements to speak of. Most information about things is going to be tag based.

Here's just the Closet class. Everything else looks more or less like this.
package persistence.table
import domain.Tag
import org.apache.commons.lang.builder.ToStringBuilder
import persistence.table.ClothingItem
import persistence.table.TaggableTable
import persistence.table.User
import javax.persistence.*

@Entity
public class Closet implements TaggableTable
{
@Id
@GeneratedValue (strategy = GenerationType.AUTO)
long id;

String name;

@OneToOne (cascade = CascadeType.ALL)
@JoinColumn (name = "USER_ID")
User user;

@Transient
Collection clothingItems;

@Transient
Collection tags;

public Collection getTags() { tags }

public void setTags(Collection tags) { this.tags = tags }

@Override
public String toString()
{
ToStringBuilder.reflectionToString(this);
}
}
Notice how incredibly terse the Groovy is, compared with comparable Java. All accessors are gone, except for two that I'm leaving on to be able to keep this typed with the interface. I've still got some attributes annotated @Transient, though that won't last.

I'm also using ToStringBuilder.reflectionToString() in Jakarta commons lang, which produces a simple String representation with no effort.

I'm using Hibernate as the JPA provider, so I have to use some Hibernate specific properties in the persistence.xml.
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="app1" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.connection.url" value="jdbc:hsqldb:mem:testdb"/>
<property name="hibernate.max_fetch_depth" value="3"/>
</properties>
</persistence-unit>
</persistence>
I set up some sample data in three tables: USER, CLOSET, and USER_CLOSET, to join the two. I'm just using vanilla JDBC to create the data, but notice how insanely simple the one line HSQLDb database creation is: just a simple call to get the connection. This db is in memory only, and won't last longer than the one execution.
Class.forName("org.hsqldb.jdbcDriver")
Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:testdb", "sa", "")

Statement statement = c.createStatement()
statement.execute("""create table user (id varchar primary key,
firstname varchar, lastname varchar)""")
statement.execute("insert into user values('chongo@mongo.com', 'jobob', 'chongo')")

statement.execute('''create table closet (id integer identity primary key,
name varchar, user_id varchar, foreign key (user_id) REFERENCES user(id))''')
statement.execute('''insert into closet (id, name, user_id)
values (1, 'mixed use', 'chongo@mongo.com')''')

statement.execute("""create table user_closet (user_id varchar,
closet_id integer, constraint uc_pk primary key (user_id, closet_id))""");
statement.execute("""insert into user_closet (user_id, closet_id)
values ('chongo@mongo.com', 1)""")
c.commit()

Here's a test:
try
{
EntityManagerFactory emf = Persistence.createEntityManagerFactory("app1")
EntityManager em = emf.createEntityManager()
EntityTransaction transaction = em.getTransaction()
transaction.begin()
User user = em.find(User.class, "chongo@mongo.com")
println user
Closet retrievedCloset = em.find(Closet.class, 1l)
println retrievedCloset
Closet newCloset = new Closet()
newCloset.setName("special purpose")
em.persist newCloset
em.flush()
Closet retrievedNewCloset = em.find(Closet.class, 2l)
println retrievedNewCloset
} catch (Throwable t) {
t.printStackTrace()
}

Even though Groovy turns all Exceptions into RuntimeExceptions, I found that wrapping the code in a try/catch made things vastly easier. Without it, errors and stacktraces are trimmed to nothing and it makes debugging JPA related bugs virtually impossible. This is the output:

persistence.table.User@9ec1c7[id=chongo@mongo.com,firstName=jobob,lastName=chongo]
persistence.table.Closet@2e2b5c[id=1,name=mixed use,user=persistence.table.User@9ec1c7
[id=chongo@mongo.com,firstName=jobob,lastName=chongo],
clothingItems=,tags=]
persistence.table.Closet@2e2b5c[id=1,name=mixeduse,user=persistence.table.User@9ec1c7
[id=chongo@mongo.com,firstName=jobob,lastName=chongo],clothingItems=,tags=]
persistence.table.Closet@a19ab2
[id=2,name=special purpose,user=,clothingItems=,tags=]

Of course, Grails's GORM makes this even simpler, without any annotations needed. However, I really admire the terseness of the combination of Groovy and JPA. In a shared coding environment, having annotations that mark the mappings and make them explicit could be a big plus.

0 comments:

Post a Comment

What is this site?!

t (a professional software developer for a big company) writes about software development and stuff. Unix, Java, Groovy, OS X, and lots of open source libraries and tools.