Developer: J2EE
   DOWNLOAD
 Oracle TopLink
 Oracle JDeveloper 10g (10.1.3.2)
 Spring Framework
 Sample Code
 
   TAGS
opensource, java, All
 

 

Using Oracle TopLink with the Spring Framework


by Lonneke Dikmans, Oracle Fusion Middleware Regional Director

 

Build a sample application using Spring 2.0 and Oracle TopLink with Oracle JDeveloper 10g, step by step.

Updated May 2007

Spring, a popular framework for developing lightweight J2EE applications, uses Aspect Oriented Programming (AOP) and dependency injection at its core. It supports various frameworks, including object-relational mapping (ORM) tools such as the open-source Oracle TopLink and JBoss Hibernate. In this article, you will learn how to use the Spring Framework's support for TopLink. ( Click here to visit the Oracle + Spring page on OTN.)

Spring offers several benefits:

  • It abstracts the data access code from the business logic using the DAO pattern.
  • It uses the Template pattern to implement the invariant behavior of data access.
  • It has a common exception hierarchy.
  • It offers integrated transaction management and
  • It takes care of resource management.

Using Spring doesn’t change the basics of the underlying ORM tool. This means that you can continue to design and code applications the way you’re used to.

Here you'll build an application using Spring 2.0 and TopLink with Oracle JDeveloper 10.1.3.2. First, you’ll set up the workspace and then look at a sample application. After that, you’ll examine the data access code as well as the Spring configuration. After testing the code, you’ll deploy the application to the Oracle Containers for Java (OC4J) runtime.

Set Up Your Workspace

The sample application uses JDeveloper 10.1.3 with Spring 2.0.When creating your own application, you’ll base it on the Web application template with TopLink, EJB, and JavaServer Faces (JSF). For this case, however, start by downloading and extracting the sample application and importing it into JDeveloper (see Figure 1):

  1. Extract the zip file to the folder of your choice.
  2. Click the green plus sign to import the existing application.
  3. Navigate to the folder to which you extracted the zip file.
  4. Double-click the HockeyCoachAssistant.jws file.

Figure 1
Figure 1. Import an existing application.

Once you import the application, you must add libraries to use the Spring framework and to test the application. Because you used the Web application template, the libraries for TopLink and JSF have already been added to the Model and ViewController projects. Table 1 shows the libraries you need to add.

Table 1. Libraries needed for each project.

Project

Library Name

Jar Location

Remark

Model

Spring 2.0

Download Spring extension for JDeveloper

Use extension from JDeveloper Update Center or the Oracle Developer Kit for Spring

TestModel

JUnit Runtime

Download Spring extension for JDeveloper

Use extension from JDeveloper Update Center

TestModel

DBUnit

Class: [dbunithome]\dbunit2.1.jar

http://www.dbunit.org

ViewController

Spring 2.0

See Model project

See Model project

Start by adding libraries for the Model project:

  1. Add Spring as a library.
    • Install Spring 2.0 using "Check for updates" from the Help menu.
    • Add the Spring 2.0 libraries to the project.

The Model project now looks like Figure 2.

Figure 2
Figure 2. Libraries needed to compile and run the Model project.

Next, add libraries to the TestModel project:

  1. Add the JUnit library.
  2. Install JUnit runtime using Check for Updates from the Help menu.
  3. Add the JUnit Runtime library to the project.
  4. Add the DbUnit library.
    • Download DbUnit from www.dbunit.org.
    • Create a user-defined library called dbunit.
    • Add the dbunit2.1.jar to the class path.
    • Optionally, download the sources and add them to the library as well.

The TestModel project now looks like Figure 3.

Figure 3
Figure 3. Libraries needed to compile and run the TestModel project.

Finally, add the necessary libraries to the ViewController project:

  1. Add the spring 2.0 library you downloaded from the JDeveloper Update Center.

The ViewController project should now look like Figure 4:

Figure 4
Figure 4. Libraries needed to compile and run the ViewController project.

The Sample Application

Now that you’ve set up the workspace, you can focus on the HockeyCoachAssistant application. This simple program keeps track of teams in a field hockey competition; users can add, delete, or edit teams from the competition.

The user interface is a Web application that consists of java server pages (jsp) with ADF Faces components. The Start page is teams.jsp (see Figure 5).

Figure 5
Figure 5. Navigation in the application as defined in faces-config.xml.

The application consists of four layers: model, data access (DAO), service, and view (see Figure 6). This article focuses on the DAO layer, which is where the Spring support for TopLink is used.

Figure 6
Figure 6. Logical view of the HockeyCoachAssistant application.

Installing the Application

To show how TopLink support is used in the HockeyCoachAssistant application, you must create a connection to the database and create the schema and tables.

  1. Create the database schema by running the SQL scripts in the Database folder of the Model project.
    • Create a user called “comp” by running the file compUser.sql with a tool such as SQLPlus or SQLDeveloper. Make sure you’re connected as System or another user that has privileges to create users.
    • Connect as user “comp” and run the createTable.sql script with the same tool.
  2. Create a database connection in JDeveloper using the Connections tab.
    • Create a new database connection called Competition.
    • Enter the username (“comp”), password (“comp”).
    • Enter the SID to point to your database.
  3. Correct the connection settings in sessions.xml (See Figure 7).
    • Open the sessions.xml in the Model project’s src\META-INF folder.
    • Double-click on the session named localSession in the Structure pane.
    • Click Login.
    • Update the JDBC connection URL to point to your database.
    • If necessary, update the database platform.

    Figure 7
    Figure 7. Correct the URL for the localSession.

  4. Correct the settings for the competitionMap (See Figure 7).
    • Click the TopLink map.
    • Edit the connection URL.
    • If necessary, change the database platform.

    Figure 8
    Figure 8. Correct the competitionMap.

  5. Edit the test-toplink-context.xml file in the TestModel project.
    • Double-click the file.
    • Change the database settings to match your local installation (see Figure 9).

    Figure 9
    Figure 9. Correct the URL for the JDBC connection in test-context.xml.

  6. Check the installation.
    • Rebuild the workspace.
    • Generate a TopLink mapping status report.
    • Run the tests in CompetitionDaoTopLinkTest and make sure they all fail with an org.springframework.beans.factory.BeanCreationException (see Figure 10).

    Figure 10
    Figure 10. Errors you’ll get, until you implement the CompetitionDao interface.

    Now you’re ready to implement the CompetitionDao class.

The Data Access Layer

Spring support for data access includes two parts:

  • A Template class that handles common elements of data access in J2EE applications, including transaction management, resource management, and exception handling
  • Callbacks to implement the application-specific details

This is true for all of Spring’s data access support, including TopLink, Hibernate, JDBC, IBATIS, and JDO. There are two ways to use the TopLinkTemplate class: You can inject it in every DAO class in the application using the Application Context, or you can extend a support class.

Our example extends the TopLinkDaoSupport class. This results in the following structure for the data access component (Figure 11):

Figure 11
Figure 11. Structure of data access layer in the application.

The TopLinkCompetitionDao (see Figure 11) implements the CompetitionDao interface, which is exposed to the service layer. This way, the data access implementation details are hidden from the rest of the application.

  1. Edit the TopLinkCompetitionDao class (see Figure 12):
    • Let class extend TopLinkDaoSupport
    • Import the springframework.orm.toplink.support.TopLinkDaoSupport class.

Figure 12
Figure 12. Listing of TopLinkCompetitionDao.

Retrieving Data. To populate the first page, you must first retrieve all the teams in the database, using the findAllTeams method. If you implement this without Spring (for example, in a stateless session bean or a plain Java class), the code would look like the code in Listing 1:

Listing 1. Retrieve data without Spring. Session management is in the code.
 public List<Team> findAllTeams() {
    List<Team> result = null;
    
    Session session = getSessionFactory().acquireSession();
    result = (List<Team>)session.executeQuery("findAllTeam", Team.class);
      session.release();      

    return result;
  }

Because the Spring template handles resource management, all you have to implement in the TopLinkCompetitionDao class is the call to executeQuery. Acquiring and releasing the session is taken care of. (See Listing 2)

Listing 2. Retrieve data with Spring. Resources are managed by Spring.
 public List<Team> findAllTeams() {
    return (List<Team>)getTopLinkTemplate().execute(
      new TopLinkCallback(){
        public Object doInTopLink(Session session) {
           return session.executeQuery("findAllTeam", Team.class);
             }
         });
    }

Of course, you can add parameters or use another named query that’s defined in the TopLink map.

Because you need to fetch all the teams, you can use the template’s convenience method (see Listing 3).

Listing 3. Alternative with Spring using convenience methods.
public List<team> findAllTeams() {
     return getTopLinkTemplate().readAll(Team.class);
}
  1. Edit the TopLinkCompetitionDao class and implement findAllTeams.
    • Remove the “return null” statement.
    • Add the code that uses the TopLinkTemplate.readAll(…) method.
  2. Run the CompetitionDaoTopLinkTest class and check that the testGetAllTeams test now succeeds.

The same logic applies to findAllClubs (see Listing 4):

Listing 4. FindAllClubs() with Spring.
public List<Club> findAllClubs(){
   return getTopLinkTemplate().readAll(Club.class);
}

Next,

  1. Edit the TopLinkCompetitionDao class and implement findAllClubs (See Listing 4).
    • Remove the “return null” statement.
    • Add the code that uses the TopLinkTemplate.readAll(…) method.
  2. Run the CompetitionDaoTopLinkTest class and check that the testFindAllClubs test succeeds.

Inserting Data. Because users can add new teams to a club, you must implement saveTeam(Team). Without Spring, your code would look like Listing 5:

Listing 5. Save team without Spring.
 public void saveTeam(Team entity) {
    UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
    uow.registerNewObject(entity);
    uow.commit();

  }

As you can see, the process of acquiring resources and managing transactions is embedded in the code.

With Spring, transactions are declared in the application context using AOP. The method now looks like Listing 6:

Listing 6. Save Team with Spring.
 public void saveTeam(Team team) {
    getTopLinkTemplate().merge(team);
 }

 

  1. Edit the TopLinkCompetitionDao class and implement the saveTeam method using the merge(..) method of the TopLinkTemplate.
  2. Run the CompetitionDaoTopLinkTest class and check that the testSaveTeam test succeeds.

You can use several variations: deepMerge, shallowMerge, and mergeWithReferences. Which one you use depends on what you want to happen with associated objects. These methods work the same as the corresponding methods on the UnitOfWork API.

Updating Data. Users can update teams by adding or changing a remark. Without Spring, the code looks like Listing 7:

Listing 7. Code to update the team without Spring.
 public void updateTeam(Team entity) {
   UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
   Object workingCopy = uow.readObject(entity);
   if (workingCopy == null){
      throw new RuntimeException("Couldn?t find team to update");
    }
    uow.shallowMergeClone(entity)
    uow.commit();
  }

Using Spring, the implementation looks like Listing 8.

Listing 8. Update team with Spring.
public void updateTeam(Team team) {
   getTopLinkTemplate().shallowMerge(team);        
}

It’s important that the entity that’s merged is registered with the session before you change it, otherwise the call to the method in Listing 8 will throw an exception. That’s why, when a user selects a team for editing, it’s read by calling the loadTeam(…) as is shown in Listing 9.

Listing 9. Load the team to register it with the session.
public Team loadTeam(Long teamId) {
  Team t = null;
  try {
   t = (Team)getTopLinkTemplate().readAndCopy(Team.class, teamId);
   } catch (ObjectRetrievalFailureException e) {
        if (logger.isDebugEnabled()) {
        logger.debug("team with id: " + teamId + "is not found");
            }
    }
    return t;
}
  1. Edit the TopLinkCompetitionDao class and implement the updateTeam method. Use the shallowMerge(..) method of the TopLinkTemplate (See Listing 8).
  2. Edit the TopLinkCompetitionDao class and implement the loadTeam method using the readAndCopy(..) method of the TopLinkTemplate (see Listing 9).
  3. Run the CompetitionDaoTopLinkTest class and check that both the testUpdateTeam and testLoadTeam tests succeed.

Deleting Data. Because teams can also be deleted, you must implement deleteTeam(Team). If we don’t use Spring, the code would look like Listing 10.

Listing 10. Delete a team without Spring.
  public void deleteTeam(Team entity) {
   UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
   Object workingCopy = uow.readObject(entity);
   if (workingCopy == null){
     throw new RuntimeException("Could not find team to delete");
   }
   uow.deleteObject(workingCopy);
   uow.commit();
  }

In Listing 11, you can see what the code looks like using the TopLinkTemplate:

Listing 11. Delete the team with the TopLinkTemplate.
public void deleteTeam(Team team) {
   getTopLinkTemplate().delete(team);
}
  1. Edit the TopLinkCompetitionDao class and implement the deleteTeam method using the delete(..) method of the TopLinkTemplate.
  2. Run the CompetitionDaoTopLinkTest class and check that the testDeleteTeam method succeeds.

Since all the tests succeed, you’re done implementing the TopLinkCompetitionDao class. Now, let’s take a closer look at the implementation of the CompetitionDaoTopLinkTest class.

 

Configuring Resources and Transactions. Once you've removed all the resource management and transaction management code from the DAO class, you must configure it in the Spring configuration file. This is done in the file ApplicationContext.xml.

  1. Open applicationContext.xml. It’s located in the ViewController project under WEB-INF (see Figure 13).

    Figure 13
    Figure 13. applicationContext in the ViewController project

    In this example, the container handles transactions and resources. You configure the Spring context to use the JtaTransactionManager. This means that the TopLink session must be configured accordingly (see Figure 14); it needs to point to a data source and use an external transaction controller.

  2. Check the competitionSession in the sessions.xml (in the Model project) and make sure it’s set up to use the External Transaction Controller.

    Figure 14
    Figure 14. Settings to use JTA transactions.

  3. Add the JtaTransactionManager to the applicationContext.xml by adding the following lines of code shown in Listing 12.

    Listing 12. Fragment from the applicationContext.xml that defines the transaction manager.
    <!-- JTA transaction manager for J2EE environments --> 
    <bean name="myTransactionManager" 
    class="org.springframework.transaction.jta.JtaTransactionManager"/>
               
    

    The data source is obtained from the container using JNDI. This is done by defining a bean of the class JndiObjectFactoryBean.

  4. Add the data source to the applicationContext.xml (see Listing 13):

    Listing 13. Code that looks up the datasource using the JNDI name.
    <!-- JNDI DataSource for J2EE environments -->
    <jee:jndi-lookup jndi-name="jdbc/competitionDS" id="dataSource"/>
    

    Both the data source and the session are defined in the LocalSessionFactoryBean.

  5. Add the sessionFactory to the applicationContext.xml:

    Listing 14. Sessionfactory configuration.
    <!-- the sessionfactory -->
    <bean name="sessionFactory" 
    class="org.springframework.orm.toplink.LocalSessionFactoryBean">
     <property name="configLocation">
    <value>sessions.xml</value>
      </property>
      <property name="sessionName">
    <value>competitionSession</value>
      </property>
      <property name="dataSource" ref="dataSource"/>
      <property name="sessionLog">
            <bean class=
    "org.springframework.orm.toplink.support.CommonsLoggingSessionLog"/>
      </property>
    </bean>
    

    This session factory is injected into the TopLinkCompetitionDao.

  6. Define the competionDao bean and set the session factory by adding the code from Listing 15:

    Listing 15. Configuration of the DAO class.
    <!--data access class with session factory -->
    <bean name="competitionDao" 
    class="com.xebia.demo.hockey.dao.toplink.TopLinkCompetitionDao">
          <property name="sessionFactory">
                    <ref bean="sessionFactory"/>
            </property>
    </bean>
    

    The competition service is defined in this context as well. The competitionDao you defined in Listing 15, is injected into the service using setter injection.

  7. Define the competitionService by adding the following definition to the applicationContext.xml:

    Listing 16. Definition of the competition service.
    <!-- the service class that is injected when 
    the JSF pages call competitionService -->
    <bean name="competitionServiceTarget" 
          class="com.xebia.demo.hockey.service.impl.CompetitionServiceImpl" >
      <property name="competitionDao">
    <ref bean="competitionDao"/>
      </property>
    </bean>
    

    Transactions are defined for every method on the CompetitionService class using AOP. You intercept the CompetitionService and use a new transaction for every method using the wildcard “*” (See Listing 17).

  8. Add the interceptor by adding the following code to the applicationContext.xml:

    Listing 17. Transactions are defined using AOP.
    <!-- the bean that intercepts the competitionService to wrap a transaction 
    around the methods -->
    <bean id="competitionService" 
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
            <property name="transactionManager">
              <ref bean="myTransactionManager"/>
            </property>
            <property name="target">
              <ref bean="competitionServiceTarget"/>
            </property>
            <property name="transactionAttributes">
            <props>
                   <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
            </property>
     </bean>
    

    The resulting configuration file should look like that in Figure 15.

    Figure 15
    Figure 15. The resulting configuration in the file applicationContext.xml.

Testing the Code

Now it’s time to examine the test code. Figure 16 shows the class model of the tests. There’s a test method for every method in the TopLinkCompetitionDao class. The tests are run against an Oracle database. In this example, I used DbUnit to initialize the data from an XML file. JUnit is used to write test cases and assert the results. The class AbstractTransactionalDataSourceSpringContextTests makes sure the tests run in a transaction and are rolled back after they complete. In these tests, I used the jdbcTemplate to retrieve data and count rows.

Figure 16
Figure 16. Class model for the test code.

For the tests, Spring is configured differently. AbstractDaoTest defines where the spring configuration is located in the getConfigLocations() method. It points to the file test-config.xml. The most important differences from the applicationContext are:

  • It uses the org.springframework.jdbc.datasource.DriverManagerDataSource.
  • It uses TopLinkTransactionManager instead of JtaTransactionManager.
  • No transaction boundaries are defined because they’re handled by the AbstractTransactionalDataSourceSpringContextTests class.

Using this approach, the test code becomes very simple. Listing 18 shows an example of the testGetAllTeams():

Listing 18. Example of a test method in CompetitionDaoTopLinkTest.
/**
* Tests the method that gets all teams that belong to a competition.
* Fields that are fetched include the club and the name
*/
public void testGetAllTeams(){
  List<Team> teams = competitionDao.findAllTeams();
        
  assertNotNull(teams);
  assertEquals(2, teams.size());
        
  Team teamOne = teams.get(0);
  assertNotNull(teamOne.getClub());
  assertEquals("Kampong", teamOne.getClub().getName());
  assertNotNull(teamOne.getName());
 }

Several convenience methods are available in the AbstractXXXSpringContextTests classes. These methods, which are used in several of the tests, are described in Table 2.

Table 2. Methods used in the test class.

Method Name

Explanation

Used in Method

deleteFromTables(String[] tables)

Deletes data from the tables in the String array.

testFindAllClubs

setComplete()

Prevents the transaction from rolling back.

testSaveTeam

testDeleteTeam

endTransaction()

The transaction ends with no rollback if setComplete() is called.

testSaveTeam

testDeleteTeam

startNewTransaction()

Starts a new transaction within a method. Any number of transactions may be created and ended in this way. The final transaction will automatically be rolled back when the test case is torn down.

testSaveTeam

testDeleteTeam

Play around with the code to see what happens if you comment out the setComplete() and the endTransaction() in the the testSaveTeam or testDeleteTeam method. The tests should fail because the jdbcTemplate doesn’t use the TopLink session and the transaction is still running.

Deploying the Application

Now that you’ve tested the application, you can deploy it using deployment profiles, ant, or maven (See Figure 17). For this case, use deployment profiles.

  1. Edit the webapp.deploy profile.
    • Select the Spring 2.0 library.

    Figure 17
    Figure 17. Libraries that need to be deployed with the application.

  2. Start the standalone OC4J. Make sure it runs with JDK1.5.
  3. Create a new connection to the OC4J with the Connections tab in JDeveloper.
  4. Deploy the application to the standalone OC4J using the deployment profile.
  5. Test the application.
    • Open a Web browser.
    • Type http://localhost:8888/competition/faces/teams.jsp.
    • Edit, add, and delete some teams (see Figure 18).

    Figure 18
    Figure 18. The Start page of the application.

What’s Next

Spring Framework 2.0 supports TopLink, Hibernate, JDO and other data access frameworks as well as the new kid on the block: Java Persistence API (JPA). It offers comprehensive support for the Java Persistence API the same way it supports TopLink and Hibernate. It also includes the open-source TopLink Essentials, the reference implementation of JPA.

Conclusion

In this article, you've learned how to use Spring and TopLink in JDeveloper. Setting up the workspace is easy and you’ll appreciate being able to design and code TopLink applications the way you’re used to. Resource and transaction management can be defined declaratively in the Spring configuration, making for a clean separation of concerns. Apart from making the code easier to maintain and test, this makes migrating to EJB 3.0 and JPA easier.


Lonneke Dikmans, an Oracle Fusion Middleware Regional Director and Oracle ACE, is managing partner at Approach Alliance in The Netherlands. She is an architect, specializing in SOA and agile development. Lonneke has been using JDeveloper since 2000, and has experience designing, developing, and deploying Java applications.