JHU Object-Oriented Software Engineering Course Homepage
Creating Service Beans
Table of Contents

Data Retrieval


12. Persistence

Our next objective will be to use our configured MySQL database to store some information. We will store a data structure representing the guesses that visitors have submitted. Create a package named edu.jhu.cs.oose.j2ee.example.vo in your VO project's root package and put the following class inside of it.

public class Guess { private String name; private boolean correct; public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isCorrect() { return correct; } public void setCorrect(boolean correct) { this.correct = correct; } }

This is a fairly simple class. Note that it has an implicit no-args constructor; this is necessary for Hibernate to be able to use it.

Next, we will add a unique ID number to the class since it doesn't have one of its own. While Java objects usually don't need such an identifier (since they have a unique location in memory), persisted objects typically do. A different ID can be used if one is handy (such as a username or a student ID code), but in this case we must create an ID field simply to be able to tell Guess objects apart. Add an id field to the class so that it now looks like this:

public class Guess { private int id; private String name; private boolean correct; public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isCorrect() { return correct; } public void setCorrect(boolean correct) { this.correct = correct; } public int getId() { return id; } public void setId(int id) { this.id = id; } }

Next, we will add the JPA annotations which allow us to describe the persistence properties of the class to Hibernate. An application exclusively using JPA annotations is not dependent on the Hibernate library, but most practical applications need to take advantage of some features not yet standardized (like Hibernate queries or specifications of the maximum length of strings). After annotating our class, we have the following.

@Entity public class Guess { @Id @GeneratedValue private int id; @Basic private String name; @Basic private boolean correct; public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isCorrect() { return correct; } public void setCorrect(boolean correct) { this.correct = correct; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
Warning: Make sure you import javax.persistence.Entity and not org.hibernate.annotations.Entity.

These annotations have the following effect:

In some configurations, we would now have to inform Hibernate that this is a persisted class. In this tutorial's configuration, however, this is addressed by the packagesToScan property of the sessionFactory bean that we set up in test-servlet.xml. That property tells Hibernate to scan the package and all of its subpackages for @Entity-annotated classes. As a result, the mere existence of the class is enough.

We have now configured the object we wish to be able to save to the database; next, we must configure a repository for these objects (sometimes also known as a DAO - a data access object). You may create as many or as few repositories as you wish (much like service and controller beans); it's generally best to create one repository for each type of object or related group of objects. In this case, we will create a single repository for persisting Guess objects.

We start by creating a package inside of the application root package of our data project. This package is called edu.jhu.cs.oose.j2ee.example.repositories. Inside of that package, we create an interface named GuessRepository and a class named GuessRepositoryImpl.

public interface GuessRepository { public void save(Guess guess); }
@Repository public class GuessRepositoryImpl implements GuessRepository { @Autowired private SessionFactory sessionFactory; @Override public void save(Guess guess) { this.sessionFactory.getCurrentSession().saveOrUpdate(guess); } }

The SessionFactory property of the above repository implementation is the object which is used to create and manage Hibernate sessions in the J2EE application. We configured this bean in the Spring XML configuration, which means that the @Autowired annotation will ensure that it is wired in correctly. This object is also used by the transaction manager, which is discussed below.

This particular repository implementation is about as contrived as the rest of the application; it performs a very simplistic function, merely delegating its call to the session factory. More complex repositories, however, may have routines for saving or deleting whole groups of objects, finding all objects which match a certain set of criteria, and so on.

We now modify the GuessService bean to use the repository to save each guess it receives.

@Service public class GuessServiceImpl implements GuessService { private static final String ANSWER = "Rumplestiltskin"; @Autowired private GuessRepository guessRepository; @Transactional public boolean guessName(String guess) { boolean correct = guess.equalsIgnoreCase(ANSWER); Guess guessObj = new Guess(); guessObj.setCorrect(correct); guessObj.setName(guess); this.guessRepository.save(guessObj); return correct; } }

The newest addition above is the @Transactional annotation. Spring uses this annotation to signal that the method's body must run within a consistent transaction. By default, the transaction is rolled back if a RuntimeException subclass is thrown and commits the transaction in all other cases (including when checked exceptions are thrown). This behavior can be adjusted by passing arguments to the @Transactional annotation.

A transaction is a way for a database to ensure that a set of operations either succeed or fail and that multiple sets of operations do not conflict with each other. In order for any of the SessionFactory calls on the service beans to function correctly, a transaction must be running at the time they are called. The Spring @Transactional annotation accomplishes this by creating a proxy bean which implements the service interface and decorates calls to the real service with transaction-handling code. As a result, calls within the bean itself from a method which is not transactional to a method that is will not properly create a transaction. In this case, the simplest solution is to obtain the CGLIB library and add it as a dependency to your J2EE application.

Launching the application and submitting guesses will now save objects to the database. Consider, for example, the following examination of the MySQL database after guesses have been applied.

mysql> select * from Guess; +----+---------+-----------------+ | id | correct | name | +----+---------+-----------------+ | 1 | | Bjorn | | 2 | | Bjorn | | 3 | | Rumplestiltskin | +----+---------+-----------------+ 3 rows in set (0.00 sec)

It is important to note that the alignment above is not a mistake; it is the effect of the correct cell in the third row having a different (unprintable) value from the first two. Hibernate is using this encoding to distinguish between true and false values.


Creating Service Beans
Table of Contents

Data Retrieval

Written by Zachary Palmer with help from Varun Sharma. Corrections and suggestions are welcome; please e-mail zachary dot palmer xX att Xx jhu.edu