Java EE Application Development – Add EJB modules and Database Operations

Datetime:2017-03-03 05:14:52         Topic: EJB  DataBase          Share        Original >>
Here to See The Original Article!!!

EJB, Enterprise Java Bean, encapsulates the business logic of an application. You can use it to invoke system-level services, such transctions and security. 1 Based on the JPA module we created last time, we now use EJB to do database operations. From this tutorial, you will be able to add a new user to the database, query a list of users and display them in the JSF page.

Project Structure

Implement DAO

Let’s add some code to the notebookDomain module to manipulate data in the database. Create an interface IUserDao under com.zxuqian.notebook.dao package with the following code:

public interface IUserDao {

    User getUserById(Long id);

    List<User> getAllUsers();

    Long addUser(User user);

    void deleteUser(User user);
}

Then create the implementation class UserDao under com.zxuqian.notebook.dao.impl package. The UserDao class constructor takes an EntityManager as the only argument. Since EJB is a container managed component, we need it to inject the EntityManager and pass it to our persistence layer.

public class UserDao implements IUserDao, Serializable {


    private EntityManager entityManager;

    public UserDao(EntityManager entityManager) {
        this.entityManager = entityManager;
    }


    @Override
    public User getUserById(Long id) {
        return this.entityManager.find(User.class, id);
    }

    @Override
    public List<User> getAllUsers() {
        List<User> userList = this.entityManager.createNamedQuery(
                "getAllUsersQuery", User.class).getResultList();
        return userList;
    }

    @Override
    public Long addUser(User user) {
        this.entityManager.persist(user);
        return user.getId();
    }

    @Override
    public void deleteUser(User user) {
        this.entityManager.remove(user);

    }
}

This class makes four method calls using EntityManager API.

  • find() method is to do a query on the database, given the primary key, and return the related Java object.
  • createNamedQuery() executes a query that is predefined. It is defined in the User class which I will show you later.
  • persist() method insert the user object to the database.
  • remove() method delete a record from the database based on the id of the given user object.

The named query is defined using @NamedQuery class annotation on the User class:

@NamedQuery(name = "getAllUsersQuery", query = "from User u")
public class User implements Serializable {

The query language used here is called JPQL which is defined by JPA specification and uses similar sytax to SQL statetments. Here we query all user objects from the database. The select clause is omiited if selecting all columns.

There is a little change in the Persistence.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="notebookDomain" transaction-type="JTA">
        <jta-data-source>java:/MySqlDS</jta-data-source>
        <properties>
            <!-- Have to define dialect and use hibernate ddl generation strategy -->
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
            <property name="hibernate.dialect" value="MySQL5" />
        </properties>
    </persistence-unit>
</persistence>

The properties are changed to be hibernate specific, as it is the default JPA provider in Wildfly. Previous configurations does not work perfectly here. For example, the default configuration will not drop tables even if you specify the “create-and-drop” in this file. The hibernate.hbm2ddl.auto will work here. The hibernate.dialect is to define the vendor of the database you use.

Create EJB modules

EJB Service client module

A EJB component can have interfaces to define wether it is a local or remote bean. Local bean can only be accessed in the same container, i.e. application server (Wildfly e.g.). Remote bean can be accessed across different containers distributed on a group of machines.

Now, let’s create the service interfaces in an maven module called notebookServiceClient . All files in src* folder except **java files will be packaged as resources. Then

The content of pom.xml is:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>notebookRoot</artifactId>
        <groupId>com.zxuqian</groupId>
        <version>0.0.2</version>
        <relativePath>../notebookRoot/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>notebookServiceClient</artifactId>

    <build>
        <resources>
            <resource>
                <directory>src</directory>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>com.zxuqian</groupId>
            <artifactId>notebookDomain</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.spec.javax.ejb</groupId>
            <artifactId>jboss-ejb-api_3.2_spec</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

As you see, it also inheris from notebookRoot module, use notebookDomain and jboss ejb implementaiton as dependencies.

Create an interface IUserService using following code:

public interface IUserService {

    User getUserById(Long id);

    List<User> getAllUsers();

    Long addUser(User user);

    void deleteUser(User user);
}

These methods are general CRUD operations to the database. There we defined four methods to retrieve all users and a single user by user id, add a new user, and delete a user.

Create another interface IUserServiceLocal that extends IUserService , annotated it as @Local . This is the local bean interface.

@Local
public interface IUserServiceLocal extends IUserService {
}

Create the remote interface IUserServiceRemote

@Remote
public interface IUserServiceRemote extends IUserService {
}

They all implement same methods, so we can just leave them blank, using method defined in the parent interface.

EJB Service module

Create a maven module notebookService . This is the implementation of notebookServiceClient . The pom.xml has the following content:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>notebookRoot</artifactId>
        <groupId>com.zxuqian</groupId>
        <version>0.0.2</version>
        <relativePath>../notebookRoot/pom.xml</relativePath>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <packaging>ejb</packaging>

    <artifactId>notebookService</artifactId>
    <build>
        <resources>
            <resource>
                <directory>src</directory>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
            <plugin>
                <artifactId>maven-ejb-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


    <dependencies>
        <dependency>
            <groupId>org.jboss.spec.javax.ejb</groupId>
            <artifactId>jboss-ejb-api_3.2_spec</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.zxuqian</groupId>
            <artifactId>notebookDomain</artifactId>
        </dependency>
        <dependency>
            <groupId>com.zxuqian</groupId>
            <artifactId>notebookServiceClient</artifactId>
        </dependency>
    </dependencies>

</project>

The difference is the “ has a value of ejb , which will be packaged as an EJB module. You may need to put a ejb-jar.xml in src/META-INF folder, but it is unnecessary if the EJB version is above 3.0. The content of ejb-jar.xml is:

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
         version="3.1">
</ejb-jar>

Now, create a Java class UserServiceBean implements IUserServiceLocal with a @Stateful annotation. This means this bean will keep its states during client calls. In this class, it injects a EntityManager using @PersistenceContext annotation which means the container is responsible to manage it. Saying container, that’s why you can’t inject it in an ordinary Java class that is not managed by the container. The class here just initiates the UserDao data access object, and invokes methods provided by it.

@Stateful
public class UserServiceBean implements IUserServiceLocal {

    @PersistenceContext
    private EntityManager entityManager;

    private IUserDao userDao;

    @PostConstruct
    private void init() {
        this.userDao = new UserDao(entityManager);
    }

    public User getUserById(Long id) {
        return this.userDao.getUserById(id);
    }

    public List<User> getAllUsers() {
        return this.userDao.getAllUsers();
    }

    public Long addUser(User user) {
        return this.userDao.addUser(user);
    }

    public void deleteUser(User user) {
        this.userDao.deleteUser(user);
    }
}

Service Invocation (JSF)

Now it’s time to using the EJB service to present and save data for us. Create a class UserBackBean in the notebook JSF module with the following code:

public class UserBackBean implements Serializable {

    private Logger logger = Logger.getLogger(UserBackBean.class.getCanonicalName());

    @EJB
    private IUserServiceLocal userService;

    private List<User> users;

    private User user;

    public UserBackBean() {
        this.user = new User();
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(User user) {
        this.users.add(user);
    }

    public String register() {
        this.userService.addUser(this.user);
        return this.getAllUsers();
    }

    public String getAllUsers() {
        this.users = this.userService.getAllUsers();

        return "user_list";
    }
}

This is the back bean of a JSF page (component). The variables and methods can be accessed through EL expressions.

  • The IUserServiceLocal is annotated by @EJB so that the conatiner can manage the object’s creation and destruction.
  • The users varable of List type preserving the result of querying all users in the database.
  • The user object is defined in the notebookDomain module, and here it is used to receive the data that the user inputs.
  • The register() method will save the user object to the database and calls another method to list all of users.
  • The getAllUsers() is to query all users in the database and return a String representing the name of result page.

Now let’s create some pages. First, create a register.xhtml file in WebContent folder with following html code:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:jsf="http://xmlns.jcp.org/jsf"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

    <h:form>
        <label for="username">Username: </label>
        <h:inputText id="usernmae" value="#{userBackBean.user.username}" /><br />
        <label for="password">Password: </label>
        <h:inputSecret id="password" value="#{userBackBean.user.password}" /><br />
        <label for="date_of_birth">Date of birth: </label>
        <input type="date" jsf:id="date_of_birth" value="#{userBackBean.user.dateOfBirth}">
            <f:convertDateTime pattern="yyyy-MM-dd"/>
        </input>
        <br />
        <label for="email">Email: </label>
        <input type="email" jsf:id="email" value="#{userBackBean.user.email}" /><br />
        <label for="phone">Phone number: </label>
        <h:inputText  value="#{userBackBean.user.phone}" /><br />

        <h:commandButton value="Submit" action="#{userBackBean.register}" />
    </h:form>
</html>

Notice the syntax to reference properties defined in the back bean, it is pretty obvious and simple. The action attribute in the “ tag invokes the register() method in the UserBackBean class.

Create a page user_list.xhtml for listing users:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">

    <h:head>
        <title>Qiantu - A simple Notebook</title>
    </h:head>
    <h:body>
        <ul>
            <c:forEach items="#{userBackBean.users}" var="user">
                <li>#{user.username}</li>
            </c:forEach>
        </ul>
    </h:body>
</html>

Here a JSTL tag library is imported. It provides a number of handful tags to manipulate data in the back bean. The

is used to iterate a collection or an array. The `items` atrribute is the collection to be iterated, and the `var` attribute is the variable name that you defined for each element in the collection. For each user, the user name is displayed in the `

  • ` tag.In the `index.xhtml` page, add the following code before
:

<p><h:outputLink value="register.xhtml">Register</h:outputLink></p>
    <h:form>
        <p><h:commandLink action="#{userBackBean.getAllUsers}">List All Users</h:commandLink></p>
    </h:form>

The “ will generate a <a> tag and will redirect to the page that defined by the value attribute.

The has to be defined in the tag. The difference beweent and is that the “ do some additional operations before redirect to another page, here it calls the getAllUsers() method of UserBackBean class. The users list variable will have values and the user_list.xhtml page can then access it.

Configure Maven

As we have two additional modules here, we need to change some configurations in the pom.xml .

For the pom.xml in the notebookRoot module, a few common plugins is added in the “ tag to provide a unified management of plugin version and configurations so that you don’t have to specify plugin versions in sub-modules.

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
            </plugin>
            <plugin>
                <artifactId>maven-ejb-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <ejbVersion>3.2</ejbVersion>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-ear-plugin</artifactId>
                <version>2.10</version>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

In addition, the newly created modules are added to the “ tag:

<dependency>
    <groupId>com.zxuqian</groupId>
    <artifactId>notebookService</artifactId>
    <version>${project.version}</version>
    <type>ejb</type>
</dependency>
<dependency>
    <groupId>com.zxuqian</groupId>
    <artifactId>notebookServiceClient</artifactId>
    <version>${project.version}</version>
</dependency>

Here the notebookService has a ejb value in its tag, so the need to be specified as ejb as well, otherwise maven will not find it.

The pom.xml in the notebookDomain module need some notice:

<build>
    <resources>
      <resource>
        <directory>src</directory>
        <excludes>
          <exclude>**/*.java</exclude>
        </excludes>
      </resource>
    </resources>

    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

The “ tag must be included to define which directory contains resource files. Here all files in src folder except file end with .java will be treated as resource. By doing so, the persistence.xml can be packaged in the jar file.

Other module’s pom.xml file just made some changes on its dependencies, so here I will not show them. You can see it on Github. If the source code has changed, you can view the history and find the right one by tags or commit messages.

Testing

After run install maven goal on notebookRoot module, deploy notebookEAR module by double click wildfly:run in the plugin lifecycles. Open a browser and type the URL:

http://localhost:8080/notebook

Click the Register link,

fill some data and click Submit botton, then you should see the user name list, including the user you just added.








New