segunda-feira, 27 de outubro de 2014

An Enterprise Application Repository to Access the Players Table

In my previous message, I explained how you could create a stand-alone JPA project using EclipseLink. Now, I'm going to pick this project and turn it into a part of a more complete Enterprise Application Repository. Overall, we are going to need four different projects, all of them interconnected:

  1. Java Persistence API
  2. Enterprise JavaBeans
  3. Dynamic Web Project
  4. Enterprise Application Repository
This is only a small tutorial with minimal implementation. Production code would require a better decoupling of layers (I will mention this below).

The Java Persistence API project

Let us start by the JPA project, which is almost the same as we had in the EclipseLink example. First you need to do File--> New--> JPA Project, to reach the following window:


After a couple of Next's you reach the following window, where you tell Eclipse that the JPA library (Hibernate) will be provided by the Runtime, i.e., WildFly:


Once we press the Finish button we may start copying (sorry for this redundancy) the Player and Team code:

package data;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.*;

/**
 * Entity implementation class for Entity: Player
 *
 */
@Entity
public class Player implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
@Temporal(TemporalType.DATE)
private Date birth;
private float height;
@ManyToOne
private Team team;
public Player() {
super();
}

public Player(String name, Date birthfloat height, Team team) {
super();
this.name = name;
this.birth = birth;
this.height = height;
this.team = team;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Date getBirth() {
return birth;
}

public void setBirth(Date birth) {
this.birth = birth;
}

public float getHeight() {
return height;
}

public void setHeight(float height) {
this.height = height;
}

public Team getTeam() {
return team;
}

public void setTeam(Team team) {
this.team = team;
}

public static long getSerialversionuid() {
return serialVersionUID;
}
@Override
public String toString() {
return this.name + " id = " + this.id + ", " + this.height + " plays for " + this.team.getName() + ". Born on " + this.birth;
}
   
}

And now the Team:



package data;

import java.io.Serializable;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Team implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
int id;
private String name;
private String address;
private String presidentname;
@OneToMany(mappedBy="team")
private List<Player> players;
public Team() {
super();
}
public Team(String name, String address, String presidentname) {
super();
this.name = name;
this.address = address;
this.presidentname = presidentname;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPresidentname() {
return presidentname;
}
public void setPresidentname(String presidentname) {
this.presidentname = presidentname;
}
public List<Player> getPlayers() {
return players;
}
public void setPlayers(List<Player> players) {
this.players = players;
}

}


I will not go into details on this code in this message, because I gave some in the previous one. You may simply copy the files from one project to the other, if you red the previous example. The one thing that is different is the persistence.xml file. It looks like this:

<?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="Players" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/PlayersEAR</jta-data-source>
<class>data.Player</class>
<class>data.Team</class>
<properties>
<property name="javax.persistence.jdbc.user" value="artur" />
<property name="javax.persistence.jdbc.password" value="***" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/PlayersAndTeams" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />

<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>

</persistence-unit>
</persistence>


Notice the reference to the database in the javax.persistence.jdbc.url. You may use exactly the same database as in the previous example. It will be useful to fill data for us here. We are going to use the Java Transactions API (TPA) and we need to refer to the jta-data-source. The name we put here (java:/PlayersEAR) must exist in a configuration file of WildFly. It could be standalone.xml, or standalone-full.xml if you need Java Message Service. Look for the tag <datasources> to insert this piece of configuration:


                <datasource jta="true" jndi-name="java:/PlayersEAR" pool-name="my_pool2" enabled="true" use-java-context="true" use-ccm="true">
                    <connection-url>jdbc:mysql://localhost:3306/PlayersAndTeams</connection-url>
                    <driver>mysql</driver>
                    <security>
                        <user-name>artur</user-name>
                        <password>***</password>
                    </security>
                    <statement>
                        <prepared-statement-cache-size>100</prepared-statement-cache-size>
                        <share-prepared-statements>true</share-prepared-statements>
                    </statement>

                </datasource>


In fact, configuration of WildFly is more complicated than this. Look for the following messages on how to make WildFly work with MySQL:


I have this configured on WildFly, but I haven't read the specific messages on WildFly:


The EJB Project

Our next step is to create the Enterprise JavaBeans project. We will need a new specific "EJB Project" for that:



We need to add a reference to the previous ServerPlayers-JPA to refer the Player class in this project (see the source code, after this picture):



We will write a very simple Enterprise JavaBean (EJB). It contains two methods: one to populate the database, the other to search for players taller than a given value. This class refers an EJB remote interface, which we present afterwards. To create the bean and the respective interface, you need to do File-->New-->Session Bean, as in the figure.



The EJB implementation. You may want to take a longer more detailed look at the query:


package ejb;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import data.Player;
import data.Team;

/**
 * Session Bean implementation class PlayersEJB
 */
@Stateless
public class PlayersEJB implements PlayersEJBRemote {
@PersistenceContext(name="Players")
EntityManager em;
    /**
     * Default constructor. 
     */
    public PlayersEJB() {
        // TODO Auto-generated constructor stub
    }
    
public static Date getDate(int day, int month, int year) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.DAY_OF_MONTH, day);

Date d = cal.getTime();
return d;
}


    public void populate() {
Team [] teams = { new Team("Sporting", "Alvalade", "Carvalho"), new Team("Academica", "Coimbra", "Simões"), new Team("Porto", "Antas", "Costa"), new Team("Benfica", "Luz", "Vieira") };
Player [] players = { 
new Player("Albino", getDate(23,4,1987), 1.87f, teams[0]), 
new Player("Bernardo", getDate(11,4,1987), 1.81f, teams[0]), 
new Player("Cesar", getDate(12,5,1983), 1.74f, teams[0]), 
new Player("Dionisio", getDate(3,12,1992), 1.67f, teams[0]), 
new Player("Eduardo", getDate(31,8,1985), 1.89f, teams[0]), 
new Player("Franco", getDate(6,1,1989), 1.95f, teams[1]), 
new Player("Gil", getDate(7,12,1986), 1.8f, teams[1]), 
new Player("Helder", getDate(14,5,1987), 1.81f, teams[1]), 
new Player("Ilidio", getDate(13,6,1991), 1.82f, teams[1]), 
new Player("Jacare", getDate(4,2,1993), 1.83f, teams[1]), 
new Player("Leandro", getDate(4,10,1984), 1.81f, teams[2]), 
new Player("Mauricio", getDate(3,6,1984), 1.8f, teams[2]), 
new Player("Nilton", getDate(11,3,1985), 1.88f, teams[2]), 
new Player("Oseias", getDate(23,11,1990), 1.74f, teams[2]), 
new Player("Paulino", getDate(14,9,1986), 1.75f, teams[2]), 
new Player("Quevedo", getDate(10,10,1987), 1.77f, teams[2]), 
new Player("Renato", getDate(7,7,1991), 1.71f, teams[3]), 
new Player("Saul", getDate(13,7,1992), 1.86f, teams[3]), 
new Player("Telmo", getDate(4,1,1981), 1.88f, teams[3]), 
new Player("Ulisses", getDate(29,8,1988), 1.84f, teams[3]), 
new Player("Vasco", getDate(16,5,1988), 1.83f, teams[3]), 
new Player("X", getDate(8,12,1990), 1.82f, teams[3]), 
new Player("Ze", getDate(13,5,1987), 1.93f, teams[3]), 
};
for (Team t : teams)
em.persist(t);
for (Player p : players)
em.persist(p);
    }
    
    
    public List<Player> playersTallerThan(float threshold) {
    System.out.println("In the EJB. Height = " + threshold);
Query q = em.createQuery("from Player p where p.height >= :t");
q.setParameter("t", threshold);
@SuppressWarnings("unchecked")
List<Player> result = q.getResultList();
return result;
    }
}


Here are the two methods that are visible from the outside:


package ejb;

import java.util.List;

import javax.ejb.Remote;

import data.Player;

@Remote
public interface PlayersEJBRemote {
public void populate();
public List<Player> playersTallerThan(float threshold);
}




The Dynamic Web Project

Now, we will provide a web access to the EJB, through a servlet. For this we must first create a Dynamic Web Project (via File-->New):



Again, we need to refer to the other two projects (more on this below):


 It would be better to use Data Transfer Objects between the EJB and the presentation layer, to ensure a strict separation between the presentation layer and the data layer, as they are not adjacent. A library like MapStruct may help here. This would make the example more complicated. Maybe in the future I will ensure this strict separation.

We now create the servlet (File-->New-->Servlet):



The source code next:


package servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import data.Player;
import ejb.PlayersEJBRemote;

/**
 * Servlet implementation class PlayersTallerThan
 */
// http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?fill=1
// url = http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?height=1.80
@WebServlet("/PlayersTallerThan")
public class PlayersTallerThan extends HttpServlet {
private static final long serialVersionUID = 1L;
@EJB
PlayersEJBRemote ejbremote;

/**
* @see HttpServlet#HttpServlet()
*/
public PlayersTallerThan() {
super();
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
response.setContentType("text/html");

if (request.getParameter("fill") != null) {
ejbremote.populate();
out.println("<h1>Populate: OK!</h1>");
}
else {
float tall = Float.parseFloat(request.getParameter("height"));
List<Player> lp = ejbremote.playersTallerThan(tall);

out.println("<h1> Players </h1>");
for (Player p : lp)
out.println(p + "<br/>");
}
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

}


I must say that this is not the most elegant code I've ever written, as I control the operation I want using two URLs and I have an HTTP GET command changing the state of the system (it should be a POST). Anyway, the get accepts two commands:

http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?height=1.80, where 1.80 might be changed to the "height" threshold you want to set. But before we are able to do this, we must populate the database with:

http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?fill=1

The parameter "fill" can actually have any value, the code will still work.

The Enterprise Application Repository (EAR) Project

Before we can put anything to run, we must create and deploy an EAR project. We start with File-->New--> Enterprise Application Project:


Once you push "Next" you are prompted to select the projects that compose the EAR. In our case we have the following three:


Once this step is finished, we may deploy the whole package, as follows (use the right button of the mouse):

Select the appropriate location in the deployments directory of the application server:

I assume that WildFly was running. Once you do the deployment, you should see the following output in WildFly, with the references to our project:


We can now go to a browser and enter the following URLs:

http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?fill=1

to fill the database, and

http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?height=1.80

should produce the following outcome:



quinta-feira, 16 de outubro de 2014

Java Persistence API with EclipseLink

In this message, I will provide a fully working example on Java Persistence API (JPA) using EclipseLink in a stand-alone program. I.e., this example can (and should) run outside any Application Server. Again, I am assuming that you are using Eclipse for Java EE developers.

The first thing you must do is to create a JPA project (change to Java EE or JPA view on the upper right corner of the Eclipse Window):




Let us call Players to our project, as we will store information of football teams on the database.


If you press "Next" a couple of times you'll end up having to chose the specific JPA implementation. EclipseLink might not be available on the first time. If that happens, you must install it, using the floppy disk image on the right:


We may now create the classes. In fact, we are going to create specially annotated classes that are Entities:


Let us call "Player" to the first one:

And now, it is time to program the Player Entity class. I will not take long explaining the details, as, with some effort, you may find explanations for all the annotations on the web. I will just say that many players can belong to the same Team, and therefore, we use the @ManyToOne annotation:
package data;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.*;

/**
 * Entity implementation class for Entity: Player
 *
 */
@Entity
public class Player implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
@Temporal(TemporalType.DATE)
private Date birth;
private float height;
@ManyToOne
private Team team;
public Player() {
super();
}

public Player(String name, Date birth, float height, Team team) {
super();
this.name = name;
this.birth = birth;
this.height = height;
this.team = team;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Date getBirth() {
return birth;
}

public void setBirth(Date birth) {
this.birth = birth;
}

public float getHeight() {
return height;
}

public void setHeight(float height) {
this.height = height;
}

public Team getTeam() {
return team;
}

public void setTeam(Team team) {
this.team = team;
}

public static long getSerialversionuid() {
return serialVersionUID;
}
@Override
public String toString() {
return this.name + " id = " + this.id + ", " + this.height + " plays for " + this.team.getName() + ". Born on " + this.birth;
}
   
}

Let us now go for the Team Entity class, which has the converse annotation @OneToMany. This defines a bi-directional relation, which is to say, we may access the Team from the Player object, whereas accessing the Player from the Team object is also possible. The mappedBy indication serves to indicate that the Entity Player is the owner of the relation. In practice, the database table storing Player data will have an additional column to keep the identifier (foreign key) of the Team the player belongs to.



package data;

import java.io.Serializable;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Team implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
int id;
private String name;
private String address;
private String presidentname;
@OneToMany(mappedBy="team")
private List<Player> players;
public Team() {
super();
}
public Team(String name, String address, String presidentname) {
super();
this.name = name;
this.address = address;
this.presidentname = presidentname;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPresidentname() {
return presidentname;
}
public void setPresidentname(String presidentname) {
this.presidentname = presidentname;
}
public List<Player> getPlayers() {
return players;
}
public void setPlayers(List<Player> players) {
this.players = players;
}

}



A few fundamental steps are missing. First, we need to create the database. I'm using MySQL. In my case, creating the database and granting permissions goes like this:


One of the most daunting tasks with JPA is to get the persistence.xml file right. This one works for me. Take care of using the right username and password. Maybe even the database, if it is not MySQL:
<?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="Players">
<class>data.Player</class>
<class>data.Team</class>
<properties>
<property name="javax.persistence.jdbc.user" value="artur" />
<property name="javax.persistence.jdbc.password" value="****" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/PlayersAndTeams" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />

<property name="eclipselink.ddl-generation" value="create-tables" />
<property name="eclipselink.ddl-generation.output-mode"
value="database" />
</properties>

</persistence-unit>
</persistence>


Since we reference a JDBC driver, we need to include one, in a few steps:


Pick "Add External JARs..." and add a MySQL or other appropriate driver. You need to store it on your disk, maybe inside the Eclipse project:



Let us now write a program that stores actual data to the database. One should notice that the Team is inserted on the Player side, as the Player owns the relation (from my experience with my concrete choices of technologies, any attempt to do the opposite, entering the Player on the Team side, will result in losing all data):
package data;

import java.util.Calendar;
import java.util.Date;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class WriteData {

public static Date getDate(int day, int month, int year) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.DAY_OF_MONTH, day);

Date d = cal.getTime();
return d;
}

public static void main(String[] args) {
Team [] teams = { new Team("Sporting", "Alvalade", "Carvalho"), new Team("Academica", "Coimbra", "Simões"), new Team("Porto", "Antas", "Costa"), new Team("Benfica", "Luz", "Vieira") };
Player [] players = { 
new Player("Albino", getDate(23,4,1987), 1.87f, teams[0]), 
new Player("Bernardo", getDate(11,4,1987), 1.81f, teams[0]), 
new Player("Cesar", getDate(12,5,1983), 1.74f, teams[0]), 
new Player("Dionisio", getDate(3,12,1992), 1.67f, teams[0]), 
new Player("Eduardo", getDate(31,8,1985), 1.89f, teams[0]), 
new Player("Franco", getDate(6,1,1989), 1.95f, teams[1]), 
new Player("Gil", getDate(7,12,1986), 1.8f, teams[1]), 
new Player("Helder", getDate(14,5,1987), 1.81f, teams[1]), 
new Player("Ilidio", getDate(13,6,1991), 1.82f, teams[1]), 
new Player("Jacare", getDate(4,2,1993), 1.83f, teams[1]), 
new Player("Leandro", getDate(4,10,1984), 1.81f, teams[2]), 
new Player("Mauricio", getDate(3,6,1984), 1.8f, teams[2]), 
new Player("Nilton", getDate(11,3,1985), 1.88f, teams[2]), 
new Player("Oseias", getDate(23,11,1990), 1.74f, teams[2]), 
new Player("Paulino", getDate(14,9,1986), 1.75f, teams[2]), 
new Player("Quevedo", getDate(10,10,1987), 1.77f, teams[2]), 
new Player("Renato", getDate(7,7,1991), 1.71f, teams[3]), 
new Player("Saul", getDate(13,7,1992), 1.86f, teams[3]), 
new Player("Telmo", getDate(4,1,1981), 1.88f, teams[3]), 
new Player("Ulisses", getDate(29,8,1988), 1.84f, teams[3]), 
new Player("Vasco", getDate(16,5,1988), 1.83f, teams[3]), 
new Player("X", getDate(8,12,1990), 1.82f, teams[3]), 
new Player("Ze", getDate(13,5,1987), 1.93f, teams[3]), 
};
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Players");
EntityManager em = emf.createEntityManager();
EntityTransaction trx = em.getTransaction();
trx.begin();
for (Team t : teams)
em.persist(t);
for (Player p : players)
em.persist(p);
trx.commit();
}

}


This program is very quiet. No output, or only a couple of log messages is a good sign.

Since we stored the data, we may now run a query to retrieve it. Notice that we are getting a team from the name, and then we print all the players of the team, despite never having inserted the data this way (we inserted it in the opposite direction):
package data;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;

public class ReadData {

public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Players");
EntityManager em = emf.createEntityManager();
Query q = em.createQuery("from Team t where t.name = :t");
q.setParameter("t", "Academica");
@SuppressWarnings("unchecked")
List<Team> resultteams = q.getResultList();
if (resultteams.size() > 0)
for (Player p : resultteams.get(0).getPlayers())
System.out.println(p);
}

}


The registered players of this outstanding team are:

Jacare id = 14, 1.83 plays for Academica. Born on Thu Feb 04 00:00:00 WET 1993
Ilidio id = 13, 1.82 plays for Academica. Born on Thu Jun 13 00:00:00 WEST 1991
Gil id = 11, 1.8 plays for Academica. Born on Sun Dec 07 00:00:00 WET 1986
Franco id = 10, 1.95 plays for Academica. Born on Fri Jan 06 00:00:00 WET 1989
Helder id = 12, 1.81 plays for Academica. Born on Thu May 14 00:00:00 WEST 1987

That's exactly what we inserted. If you got this result everything went well.

Finally, it is worthwhile to see how do the tables look like: