EE5 도 정식릴리즈되어 사용되어가고 있는 시점이니 만큼, 관련 사이트부터 정리해 놓을까
한다.
@ EJB 3.0 사이트 정리
Let's look at what a developer does to create enterprise beans using EJB 3.0 technology. Note that the material in this section reflects the technology as documented in the early draft of the EJB 3.0 Specification. The specification might change over time, and so some aspects of what a developer codes to create enterprise beans might also change.
A Stateless Session Bean
To create a session bean using EJB 3.0 technology, a developer only needs to code a bean class, annotated with appropriate metadata annotations, and a business interface. The business interface can be generated by default. The bean class is coded as a plain old Java object (POJO). |
Let's examine what a developer needs to do to develop a stateless session bean using EJB 3.0 technology. Once again, let's develop a bean named Converter
that converts currency -- from dollars to yen, and from yen to euros-- and that can be accessed remotely. This is the same stateless session bean presented in Developing an Enterprise Bean with EJB 2.1 Technology. You'll see how much easier it is to develop the bean using EJB 3.0 technology.
To create the session bean, a developer only needs to code a bean class and annotate it with appropriate metadata annotations:
@Stateless @Remote public class ConverterBean {
BigDecimal yenRate = new BigDecimal("121.6000");
BigDecimal euroRate = new BigDecimal("0.0077");
public BigDecimal dollarToYen(BigDecimal dollars) {
BigDecimal result = dollars.multiply(yenRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}
public BigDecimal yenToEuro(BigDecimal yen) {
BigDecimal result = yen.multiply(euroRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}
}
In this example, the metadata annotations are @Stateless
and @Remote
.
Significantly, there's no home interface, remote interface, or deployment descriptor to code. All that's required in addition
to the bean class is a business interface, and that can be generated by default (as is the case here).
The bean class is coded as a plain old Java object ("POJO") rather than a class that implements an interface such as javax.ejb.SessionBean
. Because the bean class doesn't implement an interface such as javax.ejb.SessionBean
, a developer no longer has to implement methods such as ejbRemove
, ejbActivate
, or ejbPassivate
in the bean class. However a developer can implement any or all of these callbacks if they are needed. If the bean class implements one of these callbacks, the EJB container calls it just as it does for EJB 2.1 technology.
Metadata Annotation
Metadata annotation is a new Java Language feature in Java 2 Platform Standard Edition (J2SE) 5.0. It's designed to give developers a way to annotate code at appropriate points in a program. A developer can define an annotation type, for example:
public @interface Copyright {
String value ();
}
An annotation type is defined with an @
sign preceding the interface
keyword. The annotation type includes one or more elements -- in this case, value
. (In annotations with a single element, the element should be named value
to avoid having to specify the name when the annotation is used.) After an annotation type is defined, a developer can use it to annotate declarations in code:
@Copyright("2004 My Corporation")
public class Converter {
...
An annotation consists of the @
sign preceding the annotation type, followed by a parenthesized list of element-value pairs. When an annotation processing tool (there's one in J2SE 5.0) encounters the annotation, it generates the code for the annotation -- here, a copyright notice that includes the string "2004 My Corporation".
The EJB 3.0 Specification defines a variety of annotation types such as those that specify a bean's type (@Stateless
, @Stateful
, @MessageDriven
, @Entity
), whether a bean is remotely or locally accessible (@Remote
, @Local
), transaction attributes (@TransactionAttribute
), and security and method permissions (@MethodPermissions
, @Unchecked
, @SecurityRoles
). (There are many more annotations defined in the specification than these.) Annotations for the EJB 3.0 annotation types generate interfaces required by the class as well as references to objects in the environment.
In many cases, defaults can be used instead of explicit metadata annotation elements. In these cases, a developer doesn't have to completely specify a metadata annotation to obtain the same result as if the annotation was fully specified. For example, by default, an entity bean (annotated by @Entity
) has a default entity type of CMP
, indicating that it has container-managed persistence. These defaults can make annotating enterprise beans very simple. In fact, in many cases, defaults are assumed when an annotation is not specified. In those cases, the defaults represent the most common specifications. For example, container-managed transaction demarcation (where the container, as opposed to the bean, manages the commitment or rollback of a unit of work to a database) is assumed for an enterprise bean if no annotation is specified. These defaults illustrate the "coding by exception" approach that guides EJB 3.0 technology. The intent is to simplify things for developers by forcing them to code things only where defaults are not adequate.
Business Interface
A business interface is a plain old Java interface (POJI). |
No component interface or home interface is required for a session bean. The one interface a session bean needs is a business interface. A business interface is a plain old Java interface ("POJI"). It does not have to extend or implement anything, and it does not throw java.rmi.Remote Exception
or java.rmi.CreateException
. A developer has the option of implementing the business interface or letting the interface be generated. In the previous example, a business interface named Converter
is generated for ConverterBean
by default. The generated interface is defined with the methods dollarToYen
and yenToEuro
. Generating business interfaces demonstrates that metadata annotation isn't the only place where defaults are assumed.
If a developer wanted to implement the business interface, the code would look like this:
@Stateless @Remote public class ConverterBean
implements Converter {
BigDecimal yenRate = new BigDecimal("121.6000");
BigDecimal euroRate = new BigDecimal("0.0077");
public BigDecimal dollarToYen(BigDecimal dollars) {
BigDecimal result = dollars.multiply(yenRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}
public BigDecimal yenToEuro(BigDecimal yen) {
BigDecimal result = yen.multiply(euroRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}
}
public interface Converter {
BigDecimal dollarToYen(BigDecimal dollars);
BigDecimal yenToEuro(BigDecimal yen);
}
The Session Bean Client
Here's part of what a remote client (in this case, a session bean) for the EJB 3.0 technology version ConverterBean
might look like:
import converter.Converter;
import java.math.BigDecimal;
@Session public class ConverterClient {
@Inject Converter
Converter currencyConverter;
BigDecimal param = new BigDecimal("100.00");
BigDecimal amount = currencyConverter.dollarToYen(param);
...
}
JNDI is no longer required to get references to resources and other objects in an enterprise bean's context. Instead, a developer can use resource and environment reference annotations in the bean class. |
Notice that there's no JNDI lookup code in the client. JNDI is no longer required to get references to resources and other objects in an enterprise bean's context. Instead, a developer can use resource and environment reference annotations in the bean class. These annotations are known as dependency annotations, because a developer uses them to declare the dependency of the enterprise bean on a resource or other object in the environment. The container then takes care of obtaining the references and providing them to the enterprise bean. Notice the @Inject annotation. It identifies a dependency injection, that is, it "injects" a dependency that the enterprise bean has on a resource or other object in the environment. Dependency injection can dramatically simplify what a developer has to code to obtain resource and environmental references.
Although JNDI lookup is not required, it can still be used if desired.
An Entity Bean With the EJB 3.0 Persistence Model
EJB persistence technology is currently being updated for the entire Java platform as part of the EJB 3.0 specification. The new Java Persistence API will define a new single model for implementing persistence in the Java platform. The new model will take effect when J2EE 5.0 ships, which is currently planned for the first quarter of 2006. In the meantime, developers should continue to use container-managed persistance with enterprise beans. Existing container-manged persistence applications will continue to work unchanged because the current container-managed persistence functionality in EJB technology will continue to be supported (and will be improved as well). There is no need to migrate existing applications to this new API. The EJB expert group is also working hard to ensure that developers can make use of EJB technology and this new API in the same application. |
Now let's see what a developer does to develop an entity bean using EJB 3.0 technology. Once again, let's develop an entity bean named PlayerBean
that represents a player on a sports team. PlayerBean
the new lightweight persistence model, and has a many-to-many relationship to TeamBean
, which represents a sports team. This is the same entity bean presented in Developing an Enterprise Bean with EJB 2.1 Technology. Here too you'll see how much easier it is to develop the bean using EJB 3.0 technology.
To create an entity bean, a developer only needs to code a bean class and annotate it with appropriate metadata annotations. The bean class is coded as a POJO. |
To create the entity bean, a developer only needs to code a bean class and annotate it with appropriate metadata annotations. Here's what part of the bean class looks like:
@Entity public class PlayerBean {
private Long playerId;
private String name;
private String position;
private double salary;
private Collection teams;
public PlayerBean() {}
@id(generate=AUTO) public Long getPlayerId() {
return id;
}
public String setPlayerId(Long id) {
this id=id;
}
public String getName() {
return name;
}
public String setName() {
this name= name;
}
...
public Collection getTeams() {
return teams;
}
public Collection setTeams(Collection teams) {
this teams=teams;
}
...
The @Entity
metadata annotation marks this as an entity bean. As is the case for a session bean in EJB 3.0 technology, there's no home interface, remote interface, or deployment descriptor to code. In fact, you don't even need a business interface for an entity bean. The bean class is coded as a POJO rather than a class that implements an interface such as javax.ejb.EntityBean
. This means, among other things, that instances of an entity bean can be created simply through a new()
operation. In addition, a developer no longer has to implement methods such as ejbRemove
, ejbActivate
, ejbPassivate
, or ejbLoad
in the bean class.
In EJB 3.0 technology, an entity bean class is a concrete class. It's no longer an abstract class. Notice that an entity bean, as illustrated by PlayerBean
, has non-abstract, private instance variables, such as PlayerID
. These variables represent the bean's persistent fields. The persistent fields are accessible through access methods such as getPlayerID
and setPlayerID
. Access methods were also used in this way in EJB 2.1 technology. However in EJB 3.0 technology, access methods are concrete, not abstract. In addition, these get
and set
methods can include logic, something that wasn't possible previously. This is useful for actions such as validating fields. Another improvement is that access to the persistence fields is not limited to the get
and set
methods. The persistence fields are also accessible through a bean class's business methods. One restriction however is that in EJB 3.0 technology, only methods within the class can access persistence fields -- in other words, you can't expose the instance variables outside of the class. That's why the instance variables are private
(they can also be protected
). Also notice the constructor in the code. Entity beans in EJB 3.0 technology must define a constructor with no parameters.
The @Id
metadata annotation is used for the primary key. The element-value pair generate=AUTO
tells the EJB container to pick a strategy for generating a primary key that's most appropriate for the database used by this application. @Id
is one of the annotation types for object-relational mapping that's defined in the EJB 3.0 specification.
The Entity Bean Client
Here's what part of RosterBean
, the session bean that locally accesses PlayerBean
, might look like using EJB 3.0 technology:
@Stateless public class RosterBean {
@Inject EntityManager em;
public Long createPlayer(PlayerBean player) {
em.create(player);
return player.getPlayerID();
public PlayerBean findByPlayerId(Long playerId) {
return (PlayerBean) em.find("Player", playerID);
}
...
}
This version of the entity bean client is much smaller and easier to code than the version that uses EJB 2.1 technology. What makes the EJB 3.0 technology version smaller and simpler to code are two new features introduced in EJB 3.0 technology: dependency injection and the EntityManager.
Dependency Injection
As mentioned earlier, the @Inject
metadata type identifies a dependency injection -- it injects a dependency that the enterprise bean has on a resource or other object in the environment. The container then takes care of obtaining the reference to the resource or object and provides it to the enterprise bean. Dependency injection reflects a fundamental change in philosophy in the EJB architecture. Previous versions of the EJB architecture forced the developer into complying with the requirements of the EJB container in terms of providing classes and implementing interfaces. By comparison, dependency injection reflects the fact that the bean tells the EJB container what it needs, and then container satisfies those needs.
Dependency injection, a new feature introduced in EJB 3.0 technology, reflects a fundamental change in philosophy in the EJB architecture. Previous versions of the EJB architecture forced the developer into complying with the requirements of the EJB container in terms of providing classes and implementing interfaces. By comparison, dependency injection reflects the fact that the bean tells the EJB container what it needs, and then container satisfies those needs. |
In the RosterBean
class the @Inject
annotation tells the EJB container that the session bean needs the EntityManager object. In general, a dependency annotation identifies a needed resource or other object in the class's environment, and the name that is used to access that resource or object. The @Inject
annotation is used when all the needed elements of the annotation can be inferred. Two other annotations that can be used for dependency injection are @Resource
, which identifies a dependency on a resource, and @EJB
, which identifies a dependency on a session bean. A developer can specify a dependency injection on a setter method of a bean class or on an instance variable in a bean class.
It's the responsibility of the EJB container to inject the reference to the needed resource or object before the session bean is made available to handle a business method. This is typically at the time that the EJB container invokes the setSessionContext
method.
Dynamic Lookup
Dependency injection is not the only way to locate resources. The EJB 3.0 specification also includes a dynamic lookup capability. This capability is offered through a lookup
method in the javax.ejb.EJBContext
interface. This provides the ability to dynamically look up a needed resource. In the following example, the setSessionContext
subinterface of EJBContext
is injected into the client code and then used to dynamically look up a session bean named myShoppingCart
:
...
@Inject
private void setSessionContext(SessionContext ctx) {
this.ctx = ctx;
}
...
myShoppingCart = (ShoppingCart)ctx.lookup
("shoppingCart");
EntityManager
The persistence model in EJB 3.0 technology has been simplified for entity beans with container-managed persistence. |
The persistence model in EJB 3.0 technology has been simplified for
entity beans with container-managed persistence. A significant aspect
of this simplified persistence model is the EntityManager
, a new API in the EJB 3.0 architecture. The EntityManager
is much like a home interface (but without a type). It is used to manage an entity bean's state. Various lifecycle methods are defined in the API, such as create
, which puts an entity bean in a managed state (that is, what's termed a "persistence context") and remove
, which removes an entity bean from the persistence context. When a bean instance is in a managed state, the data it represents is inserted into a database at the end of a transaction. The EntityManager
also provides a flush
method which synchronizes the persistence context with the underlying database. The data represented by a managed entity bean instance is inserted into a database when flush
is issued, that is, before the end of the transaction. What gets inserted at the end of a transaction or through flush
, and where in the database it gets inserted, depends on Object/Relational mapping specifications -- another new feature in EJB 3.0. When a bean instance is removed through remove
, it's no longer in a managed state -- the data it represents is removed from the database at the end of a transaction, or sooner if flush
is specified.
Some other important methods provided by the EntityManager
include find
, which finds an entity bean instance by primary key, and merge
, which merges the state of a detached entity bean instance into the current persistent context. The entity bean model in the EJB 3.0 architecture allows for detaching entity beans instances. An entity bean instance becomes detached at the end of a transaction. Detached instances can exist outside of the transaction context in which they were created. This allows a developer to do things such as serialize the bean instance to another application tier. This is a significant improvement over older versions of the entity bean model. Previously, entity beans were not serializable. Developers had to resort to a Data Transfer Object (DTO) to export an entity bean's state. In general, DTOs are not viewed positively. They're seen as "antipatterns," that is, bad solutions to a problem. The merge
method brings the state of a detached instance into the persistence context.
Looking back at the client code, RosterBean
first requests the EJB container to inject a reference to the EntityManager
:
@Inject EntityManager em;
Then RosterBean
invokes the EntityManager's create
method to put PlayerBean
in a managed state:
public Long createPlayer(PlayerBean player) {
em.create(player);
return player.getPlayerID();
It then invokes the EntityManager's find
method to find an instance of PlayerBean
by its primary key.
public PlayerBean findByPlayerId(Long playerId) {
In addition to the simplifications highlighted in Developing an Enterprise Bean with EJB 3.0 Technology, the technology includes other ease-of-development improvements. Chief among these improvements are:
Let's briefly examine each of these improvements.
Support for Testing Outside of the EJB Container
It's now much easier to test entity beans outside of an EJB container. Previously, the entity bean component model, with its requirements for home and component interfaces, abstract entity bean classes, and virtual persistent fields, made it difficult to test entity beans outside of the container. The entity bean model in the EJB 3.0 architecture removes the requirement for these interfaces. The only thing required for an entity bean is a concrete bean class that has non-abstract persistent fields. In addition, an entity bean's lifecycle is controlled through the EntityManager
API, not through a home interface whose lifecycle methods are implemented by the EJB container. All of this makes entity beans less dependent on intervention by the EJB container, and so, can be more easily tested outside of the container.
Entity beans are now less dependent on intervention by the EJB container, and so, can be more easily tested outside of the container. |
The difficulty of testing outside a container was compounded in the past by container-managed relationships. Because a container automatically manages these relationships, there's no assurance that a bean tested outside of the EJB container will behave the same and produce the same results as inside the EJB container. In supporting the testing of enterprise beans outside of an EJB container, the EJB 3.0 architecture removes support for container-managed relationships -- but still maintains support for bean-managed relationships. Dropping support for container-managed relationships was a difficult decision to make by the Expert Group responsible for the architecture. But the need to support entity bean testing outside of the EJB container is a very important requirement -- one that needed to be met.
Support for Object/Relational Mapping
The EJB 3.0 specification defines metadata annotations for Object/Relational (O/R) mapping, that is, the mapping of an entity bean to the relational database structure that underlies it. Or put another way, O/R mapping is the mapping of an object schema to a database schema. An O/R mapping specification was not part of the EJB architecture in the past. By providing it, the EJB architecture gives developers a lot more control over the data model than in the past.
An Object/Relational (O/R) mapping specification has been added in the EJB 3.0 architecture. The O/R mapping supports inheritance and polymorphism. |
Significantly, O/R mapping support in the EJB 3.0 architecture supports inheritance and polymorphism. This means that a hierarchy of entity beans, where one entity bean subclasses another, can be mapped to a relational database structure, and the same queries or methods can be used against the different classes and subclasses in the hierarchy to produce results that are specific to that class or subclass.
Metadata annotations include those for table mappings, column mappings, and associations (such as many-to-one and one-to-many). For example, here is a metadata annotation that maps an entity bean named Customer
to a database table named CUST
:
@Entity
@Table(name="CUST")
public class Customer {...}
Here's an example that specifies a one-to-many relationship between the Customer
entity bean and an Order
entity bean:
@Entity
@Table(name="CUST")
public class Customer {
private Setorders = new Hashset();
...
@OneToMany(cascade=ALL)
public SetgetOrders() {
return orders;
}
public void setOrders(Setorders) {
this.orders = orders;
}
...
The cascade=ALL
specification means that actions are cascaded through the relationship. So when a Customer
instance is created and its data is stored in the database, all the orders for that instance are also stored in the database.
Here's an example of mapping with inheritance. In the following example, ValuedCustomer
is an entity bean that extends the Customer
entity bean. The hierarchy is mapped to the CUST
table:
@Entity
@Table(name="CUST")
@Inheritance (strategy=SINGLE_TABLE,
discriminatorType=STRING,
discriminatorValue="CUST")
public class Customer {...}
@Entity
@Inheritance (discriminatorValue="VCUST")
public class ValuedCustomer extends Customer {...}
The @Inheritance
metadata annotation identifies an inheritance strategy for a class hierarchy. In this example, the strategy specified by the value of the strategy
element is SINGLE_TABLE
. This means that all the bean instances are mapped to a single table. This type of strategy requires a discriminator column in the table to identify one type of bean instance from another. The value in the column indicates the class for a specific bean instance. In the example, discriminatorType
specifies that the discriminator column contains strings. The discriminatorValue
element specifies that instances of Customer
are identified by the value CUST
, and instances of ValuedCustomer
are identified by the value VCUST
.
As mentioned earlier, defaults can be used in many places throughout the EJB architecture. Relying on defaults, the inheritance example can be as simple as this:
@Entity
public class Customer {...}
@Entity
public class ValuedCustomer extends Customer {...}
Enhanced EJB QL
EJB QL has been a very popular facet of EJB technology. However, despite its popularity, EJB QL has lacked some of the features of a full SQL query language, such as bulk update and delete operations, outer join operations, projection, and subqueries. EJB 3.0 technology adds those features to EJB QL.
EJB QL adds support for operations such as bulk update and delete, outer join, projection, and subqueries. It also adds support for outer join-based prefetching. |
For example, here's an EJB QL query that requests a bulk delete operation. It requests deletion of all customers whose status is inactive.
DELETE
FROM Customer c
WHERE c.status= 'inactive'
Here's an example of an EJB QL subquery. It's a correlated subquery that requests employees whose spouse is also an employee:
SELECT DISTINCT emp
FROM EMPLOYEE emp
WHERE EXISTS (
SELECT spouseEmp
FROM EMPLOYEE spouseEmp
WHERE spouseEmp = emp.spouse)
Another important addition to EJB QL is support for outer join-based prefetching
(termed a "FETCH JOIN"). A FETCH JOIN for a query returns a result and the prefetched data. For this to work, there must be an association between the entity that the query returns as a result and the prefeteched data. This gives developers a way of bringing in data that an application immediately needs, and related data that it will later need. Prefetching can improve application performance by reducing the number of EJB QL queries that need to be invoked (and the subsequent database transmissions that need to be made). For example, here is an outer join with a fetch that requests all customers in California and all the orders for those customers:
SELECT DISTINCT c
FROM CUSTOMER c LEFT JOIN FETCH c.orders
WHERE c.address.state = 'CA'
Beyond EJB QL enhancements, the EJB 3.0 architecture also adds a Query API to create dynamic queries and named queries (named queries are similar to static queries in EJB-SQL). Here's an example that creates a dynamic query. The query produces a result set of customers whose name is like a name that's dynamically supplied to the query.
@Inject public EntityManager em;
public List findWithName (string Name) {
return em.CreateQuery {
"SELECT c FROM Customer c" +
"WHERE c.name LIKE :custName")
.setParameter("custName", name)
.setMaxResults(10)
.listResults();
}
...
The CreateQuery
method is a factory method of the EntityManager
for creating queries. This example uses the Query API on the entity manager to generate the dynamic query. Like dynamic queries in SQL, the query is specified as a string. Notice the named parameter :custName
. Named parameters have a colon (:) prefix. This is another enhancement to EJB QL. Named parameters can be used in addition to positional parameters in EJB QL queries. There are three method calls in the example. The setParameter
method dynamically binds a specific name to the query at run time. The setMaxResults
and listResults
methods limit the result set to 10, and list the results, respectively.
Here's an example that creates a named query. The named query in the example does essentially the same thing as the dynamic query in the previous example, that is, find customers who have a specific name. To create a named query, a developer first uses a metadata annotation (@NamedQuery
) to define the named query:
@NamedQuery {
name="findAllCustomersWithName",
queryString=
"SELECT c" +
"FROM Customer c" +
"WHERE c.name LIKE :custName"
}
Next, the developer creates the named query that was previously defined:
@Inject public EntityManager em;
customers = em.createNamedQuery("findAllCustomersWithName")
.setParameter("custName", "Smith")
.setMaxResults(10)
.listResults();
}
...
This article only highlights some of the simplifications made to EJB technology in the EJB 3.0 Specification. You'll find even more improvements to the technology, and more detailed information, by reviewing the specification. Your feedback on the EJB 3.0 Specification is also very important in ensuring that EJB technology meets its objectives of simplifying the developer's experience while maintaining its power and sophistication.
Ed Ort is a staff member of java.sun.com and developers.sun.com. He has written extensively about relational database technology, programming languages, and web services.
Let's examine what a developer does to create enterprise beans using EJB 2.1 technology. The examples presented here are taken from the J2EE 1.4 Tutorial. The source code for the examples is contained in the J2EE 1.4 tutorial bundle. If you download the tutorial bundle, you'll find the example source code below the
directory, where <install_dir
> is the directory where you installed the tutorial bundle.
Note that the remainder of this article focuses on session beans and enterprise beans. Message-driven beans will not be specifically covered. Some of the same development techniques described in this section for stateless session beans also apply to message-driven beans. Also, many of the development simplifications and new features provided in EJB 3.0 technology are also applicable to message-driven beans. For information on how to develop message-driven beans in EJB 2.1 technology see Chapter 28: A Message-Driven Bean Example in the J2EE 1.4 Tutorial.
A Stateless Session Bean
Let's start with an example of a stateless session bean called ConverterBean
. This is an enterprise bean that can be accessed remotely. The bean converts currency -- from dollars to yen, and from yen to euros. The source code for ConverterBean
is in the
directory.
To create the session bean, a developer needs to code:
Home Interface
Here's the home interface for ConverterBean
:
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface ConverterHome extends EJBHome {
Converter create() throws RemoteException, CreateException;
}
The home interface:
javax.ejb.EJBHome
interface. All home interfaces must extend javax.ejb.EJBHome
.create
. A home interface must define one or more life cycle methods, such as create
and remove
, and these methods can have multiple signatures. In the case of a stateless session bean, the create
method cannot have arguments. A remote client can invoke the create
method to request creation of a session bean instance. In response, the EJB container creates the instance within the container (that is, on the server). The create
method returns an object that is the remote interface type of the bean, in this example, Converter
. This object runs locally on the client and acts as a proxy for the remote instance of the bean. A client can subsequently invoke business methods on the Converter
object locally. In response, this invokes the same business methods on the remote session bean instance in the EJB container. The create
method throws a RemoteException
and a CreateException
.Remote Interface
Here's the remote interface for ConverterBean
:
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.math.*;
public interface Converter extends EJBObject {
public BigDecimal dollarToYen(BigDecimal dollars)
throws RemoteException;
public BigDecimal yenToEuro(BigDecimal yen)
throws RemoteException;
}
The remote interface:
javax.ejb.EJBObject
interface. All remote interfaces must extend the javax.ejb.EJBObject
interface.dollarToYen
and yenToEuro
. These are the methods that the remote client can call on the session bean. However, as mentioned in the description of the home interface, these method calls actually go through a local proxy object that subsequently invokes analogous methods on the remote session bean instance in the EJB container. Each business method throws a RemoteException
.Bean Class
Here's the class for ConverterBean
:
import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import java.math.*;
public class ConverterBean implements SessionBean {
BigDecimal yenRate = new BigDecimal("121.6000");
BigDecimal euroRate = new BigDecimal("0.0077");
public BigDecimal dollarToYen(BigDecimal dollars) {
BigDecimal result = dollars.multiply(yenRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}
public BigDecimal yenToEuro(BigDecimal yen) {
BigDecimal result = yen.multiply(euroRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}
public ConverterBean() {}
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}
The bean class:
javax.ejb.SessionBean
interface. Because the session bean implements this interface, it must also implement all of the methods in the interface: ejbRemove
, ejbActivate
, ejbPassivate
, and setSessionContext
. The class must implement these methods even if it doesn't use them, as is the case here because the methods are empty.ejbCreate
method. A session bean must implement at least one ejbCreate
method. It could implement more, but each must have a different signature. For every ejbCreate
method implemented in the class, there must be a corresponding create
method defined in the home interface. Recall that a client can't directly call a session or entity bean's methods, and instead calls methods defined in the bean's interfaces. To create an instance of the session bean, a remote client calls the create
method on the home interface. In response, the EJB container instantiates the session bean and then invokes the corresponding ejbCreate
method in the bean class to initialize the instance's state. In this example, ejbCreate
is empty so there is no initial setting of data in the bean instance.dollarToYen
and yenToEuro
. A remote client invokes business methods on the remote interface (through the local proxy). In response, this invokes the same business methods on the remote session bean component in the EJB container. The container then runs the business method implementations in the bean class.Deployment Descriptor
Here's the deployment descriptor for ConverterBean
:
ConverterJAR
ConverterBean
converter.ConverterHome
converter.Converter
converter.ConverterBean
Stateless
Bean
The deployment descriptor is an XML file that specifies basic information about
the bean, such as it's name and the name of its interfaces. In fact, the descriptor's
purpose is mainly to associate the bean class with the interfaces. The descriptor in this
example also identifies this as a stateless session bean.
The Session Bean Client
To demonstrate how the bean is accessed, here's the remote client provided in the ConverterBean
example inside the J2EE 1.4 tutorial bundle:
import converter.Converter;
import converter.ConverterHome;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import java.math.BigDecimal;
public class ConverterClient {
public static void main(String[] args) {
try {
Context initial = new InitialContext();
Context myEnv = (Context) initial.lookup("java:comp/env");
Object objref = myEnv.lookup("ejb/SimpleConverter");
ConverterHome home =
(ConverterHome) PortableRemoteObject.narrow(objref,
ConverterHome.class);
Converter currencyConverter = home.create();
BigDecimal param = new BigDecimal("100.00");
BigDecimal amount = currencyConverter.dollarToYen(param);
System.out.println(amount);
amount = currencyConverter.yenToEuro(param);
System.out.println(amount);
System.exit(0);
} catch (Exception ex) {
System.err.println("Caught an unexpected exception!");
ex.printStackTrace();
}
}
}
The initial part of the code creates an instance of the session bean. Recall that a client can't directly call a session bean's methods, and instead calls methods defined in the bean's interfaces. To create an instance of the session bean, the client needs to call the create
method on the bean's home interface. To do that, the client first uses the Java Naming and Directory Interface (JNDI) to acquire an object that represents the ConverterHome
interface. The client then invokes the create
method on the ConverterHome
object. In response, the EJB container creates the session bean instance and then invokes the corresponding ejbCreate
method in the bean class to initialize the instance's state. In this example, ejbCreate
is empty so there is no initial setting of data in the bean instance. The container returns a Converter
object. To invoke a business method, the client calls the business method on the Converter
object.
An Entity Bean with Container-Managed Persistence
For the second example, let's examine an entity bean called PlayerBean
that represents a player on a sports team. PlayerBean
uses container-managed persistence. In addition, it has relationships with other entity beans, such as TeamBean
. The TeamBean
entity bean represents a sports team. The relationship to TeamBean
is many-to-many: a player can be on multiple teams (in different sports), and a team has multiple players. These relationships are maintained by the container. The source code for PlayerBean
is in the
directory.
An entity bean like PlayerBean
that is the target of a container-managed relationship must have a local interface. So PlayerBean
is accessible by local clients only. To create the entity bean, a developer needs to code:
Local Home Interface
Here's an excerpt from the local home interface for PlayerBean
:
package team;
import java.util.*;
import javax.ejb.*;
public interface LocalPlayerHome extends EJBLocalHome {
public LocalPlayer create (String id, String name,
String position, double salary)
throws CreateException;
public LocalPlayer findByPrimaryKey (String id)
throws FinderException;
public Collection findByPosition(String position)
throws FinderException;
...
public Collection findBySport(String sport)
throws FinderException;
...
}
The local home interface:
javax.ejb.EJBLocalHome
interface. All local home interfaces mustjavax.ejb.EJBLocalHome
.create
. As is the case for home interfaces, a local home interface must define one or more life cycle methods, such as create
and remove
. However unlike create
method for an entity bean can have arguments. A client can invoke the create
method to request creation of an entity bean instance. In response, the EJB container creates the instance within the container (that is, on the server). The create
method returns an object that is the local interface type of the entity bean, in this example, LocalPlayer
. It also throws a CreateException
. (Because this is a local interface, the create
method doesn't throw a RemoteException
.)find
. A local home interface for an entity bean must define at least the findByPrimaryKey
method. All finder methods except findByPrimaryKey
use EJB QL queries (defined in the deployment descriptor for the bean) to find instances of an entity bean based on specific criteria. For example, findBySport
uses an EJB QL query to find instances of PlayerBean
that represent players in a specified sport. Finder methods return the local interface type of the entity bean or a collection of those types. They also throw a FinderException
.
Although not shown in this example, a local home interface can also define home methods. A home method is similar to a business method in that it contains business logic, but unlike a business method, a home method applies to all entity beans of a particular class. (A business method applies to a single entity bean.)
Local Interface
Here's the local interface for PlayerBean
:
package team;
import java.util.*;
import javax.ejb.*;
public interface LocalPlayer extends EJBLocalObject {
public String getPlayerId();
public String getName();
public String getPosition();
public double getSalary();
public Collection getTeams();
public Collection getLeagues() throws FinderException;
public Collection getSports() throws FinderException;
}
The local interface:
javax.ejb.EJBLocalObject
interface. All local interfaces must extend the javax.ejb.EJBLocalObject
interface.getPlayerID
, getName
, getPosition
, getSalary
, and getTeams
. These methods access the bean's persistent fields (playerID
, name
, position
, and salary
) and relationship field (teams
) that are specified in the bean's deployment descriptor.getLeagues
and getSports
.Bean Class
Here's an excerpt from the PlayerBean
class:
public abstract class PlayerBean implements EntityBean {
private EntityContext context;
public abstract String getPlayerId();
public abstract void setPlayerId(String id);
...
public abstract Collection getTeams();
public abstract void setTeams(Collection teams);
...
public abstract Collection ejbSelectLeagues(LocalPlayer player)
throws FinderException;
...
public Collection getLeagues() throws FinderException {
LocalPlayer player =
(team.LocalPlayer)context.getEJBLocalObject();
return ejbSelectLeagues(player);
}
...
public String ejbCreate (String id, String name,
String position, double salary) throws CreateException {
setPlayerId(id);
setName(name);
setPosition(position);
setSalary(salary);
return null;
}
public void ejbPostCreate(String id, String name, String position,
double salary) throws CreateException {
}
...
The bean class:
javax.ejb.EntityBean
interface. Because the entity bean implements this interface, it must also implement all of the methods in the interface: ejbRemove
, ejbActivate
, ejbPassivate
, ejbLoad
, ejbStore
, setEntityContext
and unsetEntityContext
. The class must implement these methods even if it doesn't use them.getPlayerID
and setPlayerID
that access the bean's persistent fields, and methods such as getTeams
and setTeams
that access the bean's relationship field. Entity bean fields are identified as persistent fields or relationship fields in the bean's deployment descriptor. Notice that the access methods are defined as abstract
. That's because the EJB container actually implements the methods. In other words, the container actually gets and sets the data for these fields.ejbSelectLeagues
. These are methods that begin with ejbSelect
. Select methods are similar to finder methods. For example, each select method requires a corresponding EJB QL query in the deployment descriptor. However unlike finder methods, select methods cannot be invoked by a client. They can be invoked only by the methods implemented in the entity bean class. In the PlayerBean
class, the select methods appear within business methods (which the local client can invoke). Unlike select methods, finder methods are not defined in the bean class.getLeagues
. In the PlayerBean
class, these methods are essentially wrappers for select methods.ejbCreate
method and an ejbPostCreate
method. To create an instance of the entity bean, a local client calls the create
method on the local home interface. In response, the EJB container instantiates the entity bean and then invokes the corresponding ejbCreate
method in the bean class. The container initializes the instance's state by calling the set
access methods in ejbCreate
to assign the input arguments to the persistent fields. At the end of the transaction that contains the create call, the container saves the persistent fields by inserting a row into the database. The container also calls method ejbPostCreate
after the component is created. This method can be used to do things such as set a relationship field to initialize the bean instance. However, in this example, the method is empty.Deployment Descriptor
Here's an excerpt from the deployment descriptor for PlayerBean
:
TeamJAR
PlayerBean
team.LocalPlayerHome
team.LocalPlayer
team.PlayerBean
Container
...
Player
no description
position
...
playerId
...
findAll
select object(p) from Player p
...
ejbSelectLeagues
team.LocalPlayer
Local
select distinct t.league
from Player p, in (p.teams) as t
where p = ?1
...
Many
TeamBean
players
java.util.Collection
Many
PlayerBean
teams
java.util.Collection
...
Clearly there's a lot more here than in the deployment descriptor for the stateless session bean in the previous example. In addition to the basic information it specifies about the entity bean, such as it's name and the name of its interfaces, the deployment descriptor specifies the bean's abstract schema. The abstract schema for the bean identifies the bean's container-managed persistence fields, such as position
, and the bean's container-managed relationships, such as the many-to-many relationship between players
and teams
. The deployment descriptor also specifies EJB QL queries, such as select object(p) from Player p
, and the finder method each query is associated with (in this case, findAll
). Additionally, the deployment descriptor associates EJB QL queries with select methods.
The Entity Bean Client and Remote Session Bean
To demonstrate how the entity bean is accessed, here are some excerpts from the local client and from a remote session bean named RosterBean
. The client and the session bean are provided in the ConverterBean
example inside the J2EE 1.4 tutorial bundle. The client invokes methods on RosterBean
, which works in conjunction with entity beans local to it, such as PlayerBean
and TeamBean
, to access needed data.
Here is the client:
import java.util.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import util.*;
import roster.*;
public class RosterClient {
public static void main(String[] args) {
try {
Context initial = new InitialContext();
Object objref = initial.lookup("java:comp/env/ejb/SimpleRoster");
RosterHome home =
(RosterHome) PortableRemoteObject.narrow(objref,
RosterHome.class);
Roster myRoster = home.create();
The client uses JNDI to acquire an object that represents the session bean's remote interface, RosterHome
. The client then invokes the create
method on the RosterHome
object. In response, the EJB container creates the session bean instance, and invokes a corresponding ejbCreate
method in the RosterBean
class to initialize the instance's state.
The client invokes the createPlayer
method of RosterBean
to create a new player:
myRoster.createPlayer(new PlayerDetails("P1", "Phil Jones",
"goalkeeper", 100.00));
Here is the createPlayer
method in RosterBean
:
public void createPlayer(PlayerDetails details) {
try {
LocalPlayer player = playerHome.create(details.getId(),
details.getName(), details.getPosition(),
details.getSalary());
} catch (Exception ex) {
throw new EJBException(ex.getMessage());
}
}
The client then calls the addPlayer
method of RosterBean
to add player P1
to team T1
:
myRoster.addPlayer("P1", "T1");
The P1
and T1
parameters are the primary keys of the PlayerBean
and TeamBean
instances.
The client invokes the getPlayersByPosition
method of PlayerBean
to create a new player.
playerList = myRoster.getPlayersByPosition("defender");
The local home interface for PlayerBean
defines the finder method, findByPosition
, as follows:
public Collection findByPosition(String position)
throws FinderException;
The EJB container then executes the EJB QL query associated with findByPosition
in the bean's deployment descriptor. The EJB QL query is:
SELECT DISTINCT OBJECT(p) FROM Player p
WHERE p.position = ?1
So What's the Problem?
Looking at the EJB 2.1 examples, it's not too hard to understand why some developers see EJB technology as overly complex. Some of the things that feed this perception are:
javax.ejb.EJBHome
" pervade the technology.ejbActivate
and ejbPassivate
. This is required even if the bean doesn't use the interface methods. This adds to what many developers see as "code clutter."
EJB 3.0 technology aims to address these and other problems that contribute to perceived complexity.
But first, let's take a step back and look at some of the basics of EJB technology. Then let's look at how the technology works today. In other words, let's examine what a developer does to create and use enterprise beans with the current level of the technology, EJB 2.1. After that let's examine how EJB 3.0 simplifies things for developers. Table of Contents
An Overview of Enterprise JavaBeans Technology
Session Beans
An EJB container never writes a stateless session bean to secondary storage. One other distinguishing thing about a stateless session bean is that it can implement a web service. Entity Beans An entity bean represents data in a storage medium, such as a relational database. Each entity bean may correspond to a table in a relational database, and each instance of the bean corresponds to a row in that table. Entity beans are not limited to representing relational databases. They can represent data in other types of data stores. But the majority of enterprise applications that use EJB technology access data in relational databases, so this article focuses on that type of data store.
Message-Driven Beans
Enterprise Bean Interfaces and Classes
Local and Remote Enterprise Beans
The EJB Container and Persistence
EJB Query Language
October 2004 Discussion
Enterprise JavaBeans(EJB)는 분산 컴포넌트를 작성하기 위해 소개되었다. 등장 시점에는 모든 이슈들과 CORBA의 복잡성을 해결하겠다는 의지를 가지고 있었다. EJB는 몇몇 메이저 버전업을 통하면서 J2EE의 핵심이 되었고, 많은 특징들을 갖춰 나갔다. 초창기부터 많은 개발자들은 EJB에 대한 호감을 가졌고, 큰 고민없이 EJB를 적용한 어플케이션을 개발해 나갔다. 결국 많은 개발자들은 EJB를 사용한 프로젝트가 원만하지 않을 경우, EJB에 책임을 돌리게 되었다.
EJB 개발은 결코 쉽지 않으며, 릴리즈 될 때 마다 그 복잡함을 더해 갔다. EJB는 그 복잡성과 무거운 속성 때문에 코끼리에 비유되고 있다. 많은 개발자들은 EJB를 도넛에 입혀진 두터운 설탕 시럽처럼 느끼고 있다. 저탄수화물과 Atkins 다이어트에 열광하고 있는 시대에, EJB expert committee 또한 EJB 개발을 쉽고 간편하게 만들어야 함은 당연하다. EJB 3.0 expert committee는 JavaOne 2004에서 EJB 3.0 Specification의 첫번째 public draft를 발표했을 때 샘플 이미지를 배포 하였다.
언뜻 보기에 EJB의 새로운 모델은 꽤 괜찮아 보였따. 이 글에서는 개발자들에게 어필하기 위해 EJB 3.0이 작은 사이즈의 멋진 외양으로 변신하려는 지 알아보련다. 다음 글에서는 EJB 3.0이 퍼시스턴스 모델을 어떻게 간소화 하려는 지 알아 보겠다. Cleaning up the Dirty LaundryEJB 3.0이 새로이 내놓을 것들을 다루기 전에, 현재의 EJB 모델에서 볼 수 있는 복잡성을 먼저 보도록 하자.
Simplifying Developers view최신 명세로 EJB를 개발한다면 HelloWorldEJB와 같은 간단한 EJB를 개발하는 것이 얼마나 어려운 것인가를 깨닫게 될 것이다. 최소한 두개의 인터페이스와 빈 클래스, 그리고 DD가 필요하다. 대부분의 개발자들은 자신이 왜 이 모든 것을 해야 하는 지 의아해 한다. Oracle JDeveloper, Eclipse 같은 IDE 들이나, Xdoclet은 개발자들을 좀 더 편하게 만들어준다. 그렇더라도 개발자들은 컨테이너에 EJB가 디플로이 될 수 있도록 클래스들을 컴파일 하고, DD를 패키징 하여야 한다.
EJB 3.0은 복잡성에 대응하기 위해 다음과 같이 하고 있다.
Metadata AnnotationsEJB 3.0은 metadata annotaion에 큰 의존을 하고 있다. metadata annotaion은 JSR 175에서 표준화되었고, J2SE 5.0에서 채텍될 것이다. Annotaion은 XDoclet과 유사한 attribute 기반 프로그래밍의 한 종류이다. 사전 컴파일이 필요한 XDoclet과 달리, annotaion은 클래스 안에서 컴파일 시점에 자바 컴파일러에 의해 컴파일 된다. (@Retention이 설정되었을 때이다.) 개발자 관점에서 보자면, annotation은 public과 같은 제한자이며, 클래스, 필드, 메소드, 변수, 로컬변수, 생성자, enumeration, 패키지에서 사용될 수 있다. Annotation은 attribute를 자바 소스에 기술해서 사용한다. Attribute는 코드 생성, 문서화, 비즈니스 레베 보안 향상과 같은 특정 서비스나 런타임 시의 특정 비즈니스 로직 등이 있다. J2EE 1.5 (5.0)의 목표는 annotaion을 사용하여 개발 과정을 보다 간소화 하는 것이고, 결국에는 annotation이 필수 요소가 되도록 하는 것이다. Annotation은 다음처럼 @ 로 표시되어 진다. @Author("Debu Panda") @Bean public class MySessionBean EJB 3.0의 목표는 개발 과정을 간소화 하는 것이다. 이를 위해서 인터페이스와 같은 몇몇 산출물을 생성하기 위해 metadata annotation을 사용하고, DD 대신에 annotaion을 사용한다.
Using POJOs and POJIs전통적으로, 자바빈즈와 인터페이스는 종종 POJO와 POJI로 간주된다. EJB 클래스는 이제 POJO와 POJI와 유사해 질 것이다. 홈 인터페이스와 같은 불필요한 객체는 곧 제거될 것이다.
개발자는 javax.ejb 패키지에 있는 EJB 인터페이스(SessionBean, EntityBean, MessageDrivenBean)를 구현하거나, 빈 구현 클래스에 있는 annotaion을 사용해야 한다. 빈 클래스에 annotate를 달기 위해 Stateless, Stateful, MessageDriven, Entity 들 중 하나를 사용할 수 있다. 예를 들면 HelloWorld란 stateless EJB를 만든다면, 다음과 같이 EJB를 정의한다. @Remote @Stateless public class HelloWorldBean { 리모트, 또는 로컬과 같은 EJB를 위한 인터페이스는 EJBOBject, 또는 EJBLOcalObject를 구현할 필요가 없다. EJB를 위한 비즈니스 인터페이스를 제공하거나, 빈 클래스에서 인터페이스를 구현하거나, 디플로이 하는 동안 생성되면 된다. 인터페이스는 Entity Bean에서는 옵션이지만, SessionBean과 MessageDrivenBean에서는 반드시 필요하다. 세션 빈에서 인터페이스를 구현하지 않는다면, 빈 인터페이스는 자동으로 생성할 것이다. 생성된 인터페이스는 빈 클래스에 사용된 annotation에 따라 로컬이거나, 리모트가 된다. 위의 코드를 보면, HelloWorld 빈을 위해 리모트 인터페이스를 생성하도록 @Remote 가 사용되었음을 알 수 있다. 필요할 경우, 리모트와 로컬 인터페이스를 모두 생성할 수 있다.
위의 예제를 보면, 인터페이스 정의와 콜백 메소드를 구현 등의 EJB 컴포넌트를 만들기 위한 일련의 작업들이 더 이상 개발자의 몫이 아님을 알 수 있다.
생성된 인터페이스의 이름은 빈 구현 클래스의 이름으로과 연관되어 만들어진다. 생성된 인터페이스들은 개발자들에겐 더할 나위없이 적합한 것들이다. 하지만 Oracle JDeveloper와 같은 IDE에서 이러한 것들을 생성해 주는 것만큼의 효용을 볼 수는 없었다.
이번 Draft에선 클라이언트에서 EJB를 룩업하기 위해 필요한 것이 무엇인지가 명확하지 않다. 그리고 EJB를 호출하기 위해 필요한 인터페이스들을 어떻게 해야 할 지에 관해서도 막연하기만 하다. 아래 열거한 이유들 때문에 난 생성된 인터페이스를 사용하는 것을 지지하지 않는다.
Removing need for Callback methods
EJB 2.1과 그 이전 릴리스에서는 몇몇 라이프 사이클과 관련한 메소드의 구현이 반드시 필요했다. 라이프 사이클 메소드로는 ejbPassivate, ejbActivate, ejbLoad, ejbStore 등이 있다. 이런 것들이 필요없는 EJB들도 모두 구현해야 했다. 예를 들면 무상태 세션 빈에서 ejbPassivate는 필요가 없다. 빈 클래스에서 이 메소드를 구현하기 전까지는 전혀 사용되지 않는 것이다. 그래서 EJB 3.0에서 이러한 라이프 사이클 메소드들은 옵션으로 되어 있다. EJB에서 어떤 콜백 메소드를 구현했을 때에만 컨테이너가 그 메소드를 호출할 것이다.
단 하나의 예외는 Stateful 세션 빈의 ejbRemove 메소드이다. Stateful 세션빈의 비즈니스 메소드에서 Remove annotation을 사용하여 표시한다. Remove annotation은 표시된 메소드를 정상, 또는 비정상 종료한 후에 Stateful 세션빈이 제거될 수 있도록 컨테이너에게 알려주는 역할을 한다. 예를 들면 Stateful 세션빈 인스턴스의 checkOut 메소드가 실행된 후 제거될 수 있도록 다음과 같이 기술하면 된다. @Stateful public class Cart { Annotations vs. deployment descriptors 상기한 바와 같이, EJB에서 DD 대신에 annotation이 쓰이게 될 것이다. DD에서 각 attribute의 기본 값은 설정되어 있으며, 개발자들은 기본 값 이외의 다른 값을 필요로 하지 않는 한 특별히 기술하지 않았다. 이것들은 빈 클래스 자체에서 annotation을 사용하여 기술될 것이다. EJB 3.0 스펙에는 개발자가 사용할 수 있는 metadata annotaion set을 정의되어 있다. metadata annotation에는 bean type, type of interfaces, resource references, transaction attributes, security 등이 있다. 간단한 예로 특정 EJB를 위한 resource reference를 쓰고 싶다면, 다음과 같이 한다.
Oracle, BEA, IBM 과 같은 J2EE 벤더들은 벤터 자체의 DD에서 쓰이는 attribute를 위한 annotaion 들을 추가할 것이다. 그리고 개발자들은 DD를 사용하지 않기 위해서 추가된 annotation들을 사용할 것이다. 개발자들에겐 아주 흥미있는 소식이 될 것이다. 왜냐하면 그들이 꽤나 귀찮아 하던 xml 디스크립터들이 사라지기 때문이다. 하지만 몇가지 문제점은 있다. Annotation을 받아 들이기 전에 좀 더 주의할 필요가 있다.
DD를 제거하게 된다면 새로운 개발자들에겐 보다 쉽게 느껴질 것이다. 그러나 주의하지 않는다면 관리적인 측면에서 새로운 암초로 등장할 수도 있을 것이다.
Simplifying Container Managed PersistenceCMP 엔티티 빈은 EJB 3.0에서 개발자들을 감탄시킬 수 있을 만큼의 대대적인 정비가 이루어 지고 있다. 복잡하고 무거운 엔티티 빈을 싫어하는 개발자들은 TopLink나 Hibernate 같은 Persistence 프레임웍은 J2EE 어플리케이션을 위한 퍼시스턴스 프레임웍을 애용하고 있다. EJB 3.0은 CMP를 간편하게 하기 위해 TopLink나 Hibernate 같은 경량 퍼시스턴스 모델을 적용하고 있어 개발자들을 기대하게 만들고 있다. 엔티티 빈을 위한 플랜을 훑어 보자. 그리고 다른 아티클에서 퍼시스턴스 향상에 관한 세부적인 내용을 다시 다루겠다.
엔티티 빈은 POJO로 다시 태어났다. 그리고 엔티티 빈에 필수였던 컴포넌트 인터페이스는 필요없다. 이제 엔티티 빈은 상속과 다형성을 지원하는 순수한 오브젝트의 모습이 될 것이다.
Following is the source code for an entity bean: @Entity public class Employee { public void setEmpName(String EmpName) { @Dependent public Address getAddress() { public void setAddress(Address address) { public Set getProjects() { public void setProjects(Set projects) { public Double getSalary() { public void setSalary(Double salary) { 코드를 보면, 빈 클래스가 보통의 엔티티 빈과 같은 추상 클래스가 아닌 보통 클래스란 것을 알 수 있을 것이다.
EJB QL에서 쿼리 능력 향상, 엔티티 빈에서의 SQL 질의 지원 등 몇가지 향상된 기능이 추가 되었다. 새로운 EntityManager API(Hibernate, TopLink의 Session API의 단순화된 버전과 유사)는 엔티티 빈을 조작하기 위해 제안되었다. (예: creattion, removal, finding of entity beans)
다음에 나올 아티클에서 제안된 CMP 엔티티 빈에 관해 더 상세히 보겠다.
Simplifying Client View for EJBsEJB를 사용하는 건 EJB가 어플리케이션에 들어 있다 하더라도 매우 복잡하다. J2EE 1.4와 EJB 3.0 스펙은 EJB를 바라보는 클라이언트 측을 보다 더 단순화시켜 놓았다.
지금은 EJB를 사용하고 싶을 경우 DD에서 ejb-ref나 ejb-local-ref를 정의해 놓아야 한다. 예를 들어 HelloWorld EJB를 호출하고 싶다면, 보통 다음과 같은 방법으로 EJB를 호출하게 된다.
먼저 EJB 레퍼런스를 DD에 등록한다.
다음 처럼 EJB를 lookup 한다. 빈 인스턴스의 EJB 룩업과 생성을 위한 예외를 명시적으로 처리해 주어야 한다. try { 다른 방법으로 EJB 3.0은 EJB를 룩업하고 호출할 수 있는 setter injection을 쓰는 방안을 제공한다.
다음은 다른 EJB에서 setter injection을 사용하여 HelloWorldEJB를 호출하는 방법이다. @Inject private void setSessionContext(SessionContext ctx) { 코드를 보면 setSessionContext 메소드에 @Inject가 붙어 있다. @Inject는 이 메소드가 dependency injection에 사용됨을 나타내는 것이다. @Inject 처리된 메소드는 EJB의 있는 메소드가 호출되기 전 EJBContext를 세팅할 때 호출된다.
HelloWorld 세션빈을 inject 하기 위해서 @EJB public HelloWorld myHello 처럼 해도 된다. 이렇게 하면 HelloWorld 빈 인스턴스가 myHello에 inject 된다.
dependency injection은 DataSource, JMS, Mail, Web Service 같은 타입의 environment나 resource reference 를 룩업하기 위해 사용할 수도 있다.
Testability Usability Outside ContainerEJB 개발자들의 가장 주요한 관심사는 EJB 개발이 복잡하다는 것 뿐만 아니라, 테스트가 쉽지 않다는 점에도 있다. EJB 컨테이너는 EJB를 개발하고 테스트 하는데 반드시 필요하다. 그래서 개발자는 테스트를 수행할 배포 플랫폼에 익숙해야만 한다. 이 부분은 많은 엔터프라이즈 개발자들에게 주요한 이슈가 아닐 수도 있다. 그렇지만 다양한 벤더를 지원해야 하는 ISV들에는 이슈가 된다. 그들은 다양한 환경에서의 동작을 보증하기 위해 그들의 EJB 테스팅을 수행해야 하기 때문이다. EJB 3.0 스펙은 컨테이너 외부에서의 테스팅 기능 제공을 약속하고 있다. 그러나 아직 Draft 스펙에는 누락되어 있다.
Conclusionpacking, assembly, API 등 많은 정보가 누락되긴 했지만, EJB 3.0에 담긴 제안들은 엔터프라이즈 자바 개발자들에겐 매우 환영할 만한 것들이다. 제안들로 인해 개발자들에게 부여되었던 복잡성을 컨테이너 벤더들에게 넘겨버리게 되므로 확실히 단순화 될 것이다. 컨테이너 벤더들이 이것들을 어떻게 구현할지, 엔터프라이즈 어플리케이션을 개발할 때 EJB 3.0을 보다 수월하게 할 수 있는 방법들을 볼 수 있게 될 것이다.
References and further reading
Author BioDebu Panda is a principal product manager of Oracle Application Server development team, where he focuses his efforts on the EJB container and Transaction Manager. He has more than 13 years of experience in the IT industry and has published articles in several magazines and has presented at many technology conferences. His J2EE-focused weblog can be found at http://radio.weblogs.com/0135826/. 카테고리태그목록최근에 올라온 글최근에 달린 댓글최근에 받은 트랙백달력글 보관함링크
Total :
Today : Yesterday : |