Rails and JPA (Instead of ActiveRecord)
Out of sheer morbid curiosity, I wondered what it would take to replace ActiveRecord with JPA in the classic blog demo. Jeroen Zwartepoorte, a developer I met last week at RailsConf, convinced me to go ahead and publish my results.
In almost all cases, I tried to keep the view and controller code similar to what you would expect from generated scaffolding, so all of the JPA code is isolated in the model class. I also refrained from making any changes to the Java entity classes, but certainly that's also a route worth exploring.
Getting Started
- Download and install NetBeans 6.0 Beta 1. Grab the Full distribution so you can get the Java IDE, Ruby and GlassFish.
Creating the Java Entity Classes
For consistency, I'm going to create the same blog application that's know to every Rails developer. However, instead of generating a model and running migrations, we'll create an Entity class and a persistence unit.
- Create a new Java Application named Post. There's no need for a Main Class.
- Create a new Persistence Unit. Select jdbc:derby://localhost:1527/sample [app on App] as the database connection and click Finish. Switch the the XML view and set the toplink.jdbc.password property to app.
- Create a new Entity Class named Posts. Set the package name to entity.
- Add fields and accessors for title and body. Your completed class should look as follows:
/*
* Posts.java
*
* Created on Sep 26, 2007, 10:15:53 AM
*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
*
* @author bleonard
*/
@Entity
public class Posts implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String title;
private String body;
public void setId(Long id) {
this.id = id;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
Test the Entity Classes
- Create a new JUnit Test named PostsTest in the entity package. You can uncheck the Test Initializer and Finalizer, we will not be using them.
- Add code to initialize the Entity Manager and test the Post entity. My complete PostTest class looks as follows:
/*
* PostsTest.java
* JUnit 4.x based test
*
* Created on September 24, 2007, 7:42 PM
*/
package entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author bleonard
*/
public class PostsTest {
private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("PostPU");
private static EntityManager em = emf.createEntityManager();
private static EntityTransaction trans = em.getTransaction();
public PostTest() {
}
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testPersist() {
// Persist some data...
trans.begin();
Posts post = new Posts();
post.setId(new Long(100));
post.setTitle("George");
post.setBody("Jetson");
em.persist(post);
trans.commit();
// Retrieve the data...
Posts result = em.find(Posts.class, new Long(100));
assertEquals(post, result);
//Clean up for the next test...
trans.begin();
em.remove(post);
trans.commit();
}
}
- Add the Java DB Driver Library to the project's Test Libraries.
- Start the Database (Tools > Java DB Database > Start Server)
- Press Shift+F6 to run the test:
Create the Rails Application
Now that we appear to have a functioning entity class, let's create the Rails application that will use that class.
- Create a new Ruby on Rails application named jpa_blog.
- Generate a controller named blog with views list, new, show and edit. We'll develop each of these views in turn.
- In the Models folder, create a new Ruby class named Post (note, we are not generating a Post model).
Step 1: Listing the Entries
Code the View
- Replace the contents of list.rhtml with the following:
<h1>The Ruby Blog</h1>
<% @posts.reverse.each do |post| %>
<h2><%= post.title %></h2>
<p><%= post.body %></p>
<small> <%= link_to 'Permalink', :action => 'show', :id => post %></small>
<hr>
<% end %><br />
<%= link_to 'New post', :action => 'new' %>
Code the Controller
- Add the following to blog_controller.rb:
def index
list
render :action => 'list'
end
def list
# Declare a paginator for the posts table
@post_pages, @posts = paginate :posts, :per_page => 10
end
Code the Model
- Add the following to the top of post.rb:
require 'java'
include_class 'javax.persistence.Persistence'
include_class 'javax.persistence.EntityManager'
include_class 'javax.persistence.EntityManagerFactory'
include_class 'java.util.List'
include_class 'java.lang.Long'
include_class 'entity.Posts' - Add the accessors:
attr_accessor :id, :title, :body - Define private methods for getting the entity manager and finding all rows in the table:
private
def self.getEntityManager
emf = Persistence.createEntityManagerFactory("PostPU")
return emf.createEntityManager()
end
def self.find_all
query = getEntityManager.createQuery("SELECT p FROM Posts p")
list = query.getResultList()
posts = [] #Create a new array to return
list.each {|post|
temp = Post.new
temp.id = post.getId
temp.title = post.getTitle
temp.body = post.getBody
posts << temp
}
return posts
end
- Define the methods that will be called by the paginate method used in the controller (these are public):
# Called by `paginate'
def self.count(*args)
query = getEntityManager.createQuery("SELECT p FROM Posts p")
count = query.getResultList().size();
puts count
end
def self.find(*args)
case args.first
when :all then find_all
else find_from_id(args.first)
end
end
Configure JRuby
- Open the Options dialog to find the location of your JRuby interpreter.
- Copy the following jars to your JRuby lib directory:
- Post.jar (build your project if you don't have a Post.jar)
- derbyclient.jar (Tools > Java DB Database > Settings will give you the location of derbyclient.jar)
- toplink-essentials.jar and toplink-essentials-agena.jar (check the Library Manager for the location of TopLink Essentials)
Test the List
This will not be very exciting because the list is empty (you can tweak the unit test to add some entries). But we will verify the application runs without exceptions. With your cursor in the index action, press F6 to start WEBrick and launch the browser. Browse to http://localhost:3000/blog.

Step 2: Creating a New Entry
Code the View
- Create a new partial, _form.rhtml, to be used by both the new and edit pages:
<p>Title<br/>
<%= text_field 'post', 'title' %></p>
<p>Body<br/>
<%= text_area 'post', 'body' %></p> - Replace the contents of new.rhtml with the following:
<h1>New post</h1>
<% form_tag :action => 'create' do %>
<%= render :partial => 'form' %>
<%= submit_tag "Create" %>
<% end %>
<%= link_to 'Back', :action => 'list' %>
Code the Controller
- Add the following to blog_controller.rb:
def create
@post = Post.new(params[:post])
if @post.save
flash[:notice] = 'Post was successfully created.'
redirect_to :action => 'list'
else
render :action => 'new'
end
end
Code the Model
- Define the initialize and save methods in post.rb:
def initialize(attributes = nil)
if !attributes.nil?
@title = attributes[:title]
@body = attributes[:body]
end
end
def save
p = Posts.new
p.title = title
p.body = body
em = Post.getEntityManager
em.getTransaction().begin()
em.persist(p);
em.getTransaction().commit()
return true
end
Test Adding a New Entry
- Return to the browser and click the new link. Add a new post.


Step 3: Viewing an Entry
Code View
- Replace the contents of show.rhtml with the following:
<p>
<b>Title:</b> <%= h @post.title %>
</p>
<p>
<b>Body:</b> <%= h @post.body %>
</p>
<%= link_to 'Edit', :action => 'edit', :id => @post %>
<%= link_to 'Back', :action => 'list' %>
Code the Controller
- Add the following to blog_controller.rb:
def show
@post = Post.find(params[:id])
end
Code the Model
- Add the following to_param convenience method which I copied from base.rb:
# Copied from base.rb. Allows us to specify post rather than post.id in list.rhtml.
# Enables Active Record objects to be used as URL parameters in Action Pack automatically.
def to_param
# We can't use alias_method here, because method 'id' optimizes itself on the fly.
(id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
end - Add the following private method:
def self.find_from_id(id)
result = getEntityManager.find(Posts.java_class, Long.new(id))
post = Post.new
post.id = result.get_id
post.title = result.get_title
post.body = result.get_body
return post
end
Test Viewing and Entry
- Return to your browser, refresh the page (so the Permalink is generated correctly) and click the Permalink:

Step 4: Editing an Entry
Code the View
- Replace the contents of edit.rhtml with the following:
<h1>Editing post</h1>
<% form_tag :action => 'update', :id => @post do %>
<%= render :partial => 'form' %>
<%= submit_tag 'Edit' %>
<% end %>
<%= link_to 'Show', :action => 'show', :id => @post %>
<%= link_to 'Back', :action => 'list' %>
Code the Controller
- An edit and update methods to blog_controller.rb:
def edit
@post = Post.find(params[:id])
end
def update
@post = Post.find(params[:id])
if @post.update_attributes(params[:post])
flash[:notice] = 'Post was successfully updated.'
redirect_to :action => 'show', :id => @post
else
render :action => 'edit'
end
end
Code the Model
- Add the update_attributes to post.rb:
def update_attributes(attributes)
p = Posts.new
p.id = id
p.title = attributes[:title]
p.body = attributes[:body]
em = Post.getEntityManager
em.getTransaction().begin()
em.merge(p);
em.getTransaction().commit()
return true
end
Test Editing an Entry
- Return to your browser and click the Edit link:


Resources
- Post.zip - Post Project
- jpa_blog.zip - jpa_blog Project
- Login or register to post comments
- Printer-friendly version
- bleonard's blog
- 4036 reads






Comments
Belated thanks...
by jaymcgavren - 2010-03-18 13:36
I'm moving from a Rails shop back to a Java shop and am just learning JPA... I wondered if this was practical and am pleased to see the experiment's been done for me. Many thanks!