Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Kenneth A. Kousen - Making Java Groovy - 2014.pdf
Скачиваний:
50
Добавлен:
19.03.2016
Размер:
15.36 Mб
Скачать

208

CHAPTER 8 Database access

Lessons learned (Groovy SQL5)

1The groovy.sql.Sql class makes working with raw SQL better in every way: resource management, multiline strings, closure support, and mapping of result sets to maps.

2Related examples in this book can be found in chapter 7 on Spring and chapter 9 on REST.

Rather than write all that SQL, you can instead use one of the object-relational mapping (ORM) tools available, the most prevalent of which is still Hibernate. The Java Persistence API (JPA) specification acts as a front-end on ORM tools and is the subject of the next section.5

8.3The Java approach, part 2: Hibernate and JPA

One approach to simplifying JDBC is to automate as much of it as possible. The early years of Java saw attempts to add ORM tools directly to the specification, with varying degrees of success. First came Java Data Objects, which worked directly with compiled bytecodes and are largely forgotten today. Then came Enterprise JavaBeans (EJB) entity beans, which were viewed by the community as a mess in the first couple of versions.

As frequently happens when there’s a need and only an unpopular specification available, the open source community developed a practical alternative. In this case the project that emerged was called Hibernate, which still aims to be the ORM tool of choice in the Java world when dealing with relational databases.

In regular JDBC a ResultSet is connected to the data source as long as the connection is open, and goes away when the connection is closed. In the EJB world, therefore, you needed two classes to represent an entity: one that was always connected, and one that was never connected. The former was called something analogous to ProductEJB, and the latter was a ProductTO, or transfer object.6 When getting a product from the database the ProductEJB held the data for a single row, and its data was transferred to a ProductTO for display. The transfer object wasn’t connected, so it could get stale, but at least it didn’t use up a database connection, which is a scarce commodity. Transferring the data from the EJB to the TO was done by a session EJB, where the transaction boundaries occurred. The session EJBs formed the service layer, which also held business logic. The whole process was much like that shown in figure 8.2.

The result is that the ProductEJB class and the ProductTO class were essentially identical, in that they both contained the same method signatures, even though the implementations were different. Martin Fowler (author of Patterns of Enterprise Application Architecture [Addison-Wesley, 2002], Refactoring [Addision-Wesley, 1999],

5Worst SQL Joke Ever Told: SQL query walks into a bar, selects two tables and says, “Mind if I join you?” (rimshot). (Warning: NoSQL version later in this chapter.)

6Older terms included Data Transfer Object (DTO) and Value Object (VO).

www.it-ebooks.info

The Java approach, part 2: Hibernate and JPA

209

Transactions

Product

ProductTO

EJB

Controller

Session

Database

EJB

 

 

 

CustomerTO

 

Customer

 

EJB

 

 

Transfer objects

 

Entity beans

(never connected)

 

(always connected)

Figure 8.2 Controllers contact transactional session EJBs, which acquire database data through entity EJBs. The data is copied to transfer objects and returned to

the controller.

and several other books) calls that an anti-pattern and says that it’s a symptom of a flawed design.

One of the key differences between Hibernate and EJBs is the concept of a Hibernate session. The innovation was that, rather than one class of objects that were always connected and another class of objects that were never connected, what was needed was a set of objects that were sometimes connected and sometimes not. In Hibernate, when objects are part of a Hibernate session, the framework promises to keep them in sync with the database. When the session closes, the object is disconnected, thereby becoming its own transfer object. Any time an object is retrieved through Hibernate, it becomes part of a Hibernate session.

You retrieve a Hibernate session via a session factory. The session factory reads all the mapping metadata, configures the framework, and performs any necessary preprocessing. It’s supposed to be instantiated only once, acting as a singleton.

Those readers who are familiar with the Spring framework (as discussed in chapter 7) should suddenly become interested, because managing singletons is one of the things that Spring is all about. Another of its capabilities is declarative transaction management, which fits in nicely too. The result is that designs in the EJB 2.x world were replaced by a combination of Spring for the declarative transactions and the session factory and Hibernate for the entity beans.

In version 3 of EJB the architecture was redesigned again to fit more closely with that used by Spring and Hibernate. The entity beans part led to the creation of the Java Persistence API. The JPA world uses the same concepts but labels them differently.7 The Hibernate Session becomes an EntityManager. The SessionFactory is an EntityManagerFactory. Objects that are managed (that is, in the Hibernate session) compose a persistence context.

7 Of course it does. Using the same terms would be too easy.

www.it-ebooks.info

210

CHAPTER 8 Database access

Finally, in the original Hibernate, mapping from entity classes to database tables was done through XML files. Over time XML has become less popular and has been replaced by annotations. Hibernate and JPA share many annotations, which is fortunate.

It’s time for an example, which will bring Spring, Hibernate, and JPA together. Chapter 7 on the Spring framework discusses Spring in some detail. Here I’ll just highlight the parts needed for the example.

To start I’ll need a database. For that I’ll use H2, a pure Java fileor memory-based database. Spring provides an embedded database bean to make it easy to work with H2. The relevant bean from the Spring configuration file is

<jdbc:embedded-database id="dataSource" type="H2"> <jdbc:script location="classpath:schema.sql"/> <jdbc:script location="classpath:test-data.sql"/>

</jdbc:embedded-database>

The schema and test-data SQL files define a single table, called PRODUCT, with three rows:

create table PRODUCT (

id bigint generated by default as identity (start with 1), name varchar(255), price double, primary key (id)

)

insert into PRODUCT(name, price) values('baseball', 5.99) insert into PRODUCT(name, price) values('basketball', 10.99) insert into PRODUCT(name, price) values('football', 7.99)

Spring provides a bean to represent the EntityManagerFactory, which has a handful of properties to set:

<bean id="entityManagerFactory" class= "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" />

<property name="persistenceUnitName" value="jpaDemo" /> <property name="packagesToScan">

<list>

<value>mjg</value>

</list>

</property>

<property name="jpaVendorAdapter"> <bean class=

"org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="database" value="H2" />

</bean>

</property>

</bean>

The LocalContainerEntityManagerFactoryBean8 class uses the data source bean

defined previously, scans the given packages for entities, and uses Hibernate as its implementation.

8Extremely long class names are a Spring staple. My favorite is AbstractTransactionalDataSourceSpringContextTests, which has 49 characters and is even deprecated. What’s yours?

www.it-ebooks.info

The Java approach, part 2: Hibernate and JPA

211

The entity itself is the Product class, this time with a sprinkling of JPA (or Hibernate) annotations:

@Entity

public class Product {

@Id

private int id; private String name; private double price;

//... constructors ...

//... getters and setters ...

//... toString, equals, hashCode ...

}

The @Entity and @Id annotations declare Product to be a class mapped to a database table and identify the primary key, respectively. Because, by an amazing coincidence,9 the Product attribute names and the database column names happen to match, I don’t need the additional physical annotations like @Table and @Column.

The ProductDAO interface is the same as that shown in section 8.1 on JDBC, except that now the insertProduct method returns the new database-generated primary key. The JpaProductDAO implementation class is where the action happens, and it’s shown in the next listing.

Listing 8.8 The JpaProductDAO class, which uses JPA classes to implement the DAO

@Repository

 

Spring bean detected

 

public class JpaProductDAO implements ProductDAO {

 

 

on a component scan

@PersistenceContext

 

 

Injected entity

 

 

private EntityManager entityManager;

 

public List<Product> getAllProducts() {

 

manager

 

 

 

return entityManager.createQuery("from Product p").getResultList();

}

 

 

 

 

public Product findProductById(int id) {

return entityManager.find(Product.class, id);

}

public int insertProduct(Product p) { entityManager.persist(p);

return p.getId();

}

public void deleteProduct(int id) { entityManager.remove(findProductById(id));

}

}

9 Not really.

www.it-ebooks.info

212

CHAPTER 8 Database access

The JPA implementation is wonderfully spare, but that’s because it assumes the transaction management is handled elsewhere and that Spring will handle allocating and closing the necessary database resources.

I would never be comfortable writing that much code without a decent test case. Spring’s test context framework manages the application context, allows the test fixture to be injected, and, if a transaction manager is supplied, automatically rolls back transactions at the end of each test.

To handle the transactions I used another Spring bean, JpaTransactionManager, which uses the entity manager factory previously specified:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" />

The resulting test case is shown in the following listing.

Listing 8.9 A Spring test case for the JPA DAO implementation

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

@Transactional

public class JpaProductDAOTest { @Autowired

private ProductDAO dao;

@Test

public void testFindById() {

Product p = dao.findProductById(1); assertEquals("baseball", p.getName());

}

@Test

public void testGetAllProducts() {

List<Product> products = dao.getAllProducts(); assertEquals(3, products.size());

}

@Test

public void testInsert() {

Product p = new Product(99, "racketball", 7.99); int id = dao.insertProduct(p);

Product p1 = dao.findProductById(id); assertEquals("racketball", p1.getName());

}

@Test

public void testDelete() {

List<Product> products = dao.getAllProducts(); for (Product p : products) {

dao.deleteProduct(p.getId());

}

assertEquals(0, dao.getAllProducts().size());

}

}

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]