Saturday, October 19, 2019

Stand-alone Java Persistence API with Hibernate, MySQL and Maven

In this post I will mostly repeat an exercise on the Java Persistence API (JPA), here with EclipseLink, this time using Maven, instead of a Java Project in Eclipse.

Consider the following questions regarding an imaginary football team and its players, which are a variant of what I usually give to students:

1. Create an entity to store teams. Besides the team name, teams can also have address, president’s name, and other properties you find relevant. Teams will also have players (see next questions).
2. Create an Entity that stores information about football players. Each player has a name, a date of birth, and height. This is a one-to-many relation, as teams have many players.
3. Write a program to populate the database.
4. Write a program that lists all players taller than a given height.
5. Write a program that lists all the players from your favorite team in the database.

I will use Hibernate as the JPA implementation and MySQL 8 as the database. One should notice that I will write the solution as a stand-alone program; this means that the program will interact directly with the database through JPA/Hibernate.

First things first. Let us start with the Maven file and respective dependencies. This may vary a little over time, especially the versions, which might change quite fast.

<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">
 <modelVersion>4.0.0</modelVersion>

 <groupId>is</groupId>
 <artifactId>Proj2-exercises</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>Proj2-exercises</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>org.hibernate.orm</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>6.0.0.Alpha2</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>6.1.0.Alpha6</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate.common</groupId>
   <artifactId>hibernate-commons-annotations</artifactId>
   <version>5.1.0.Final</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate.javax.persistence</groupId>
   <artifactId>hibernate-jpa-2.1-api</artifactId>
   <version>1.0.2.Final</version>
  </dependency>
  <dependency>
   <groupId>javax.validation</groupId>
   <artifactId>validation-api</artifactId>
   <version>2.0.0.Final</version>
  </dependency>
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>8.0.17</version>
  </dependency>
 </dependencies>
 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.0</version>
    <configuration>
     <source>12</source>
     <target>12</target>
    </configuration>
   </plugin>
  </plugins>
 </build>

</project>

The Team Entity can go like this:

package is.Proj2_exercises.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;
 }

}

Next, the Player Entity:

package is.Proj2_exercises.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 int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 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;
 }

}

Before putting the whole thing to run, we need a couple more things. A persistence.xml file that must go to the src/main/resources/META-INF directory. Just create this tree if you don't have any resources directory. The file looks like the following. Don't forget to adjust the name of the database, the username and the password. If you are using another database, like PostgreSQL, you will need to adjust a number of parameters. It shouldn't be that hard to do, I have seen many students doing it with relative ease.

<?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">
  <properties>
   <property name="javax.persistence.jdbc.user" value="user" />
   <property name="javax.persistence.jdbc.password" value="pass" />
   <property name="javax.persistence.jdbc.url"
    value="jdbc:mysql://localhost:3306/PlayersAndTeams?serverTimezone=UTC" />
   <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />

   <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect" />
   <property name="hibernate.hbm2ddl.auto" value="update" />
   <property name="hibernate.show_sql" value="true" />
  </properties>
 </persistence-unit>
</persistence>

I now write a kind of dummy main application to write data into the database.

package is.Proj2_exercises;

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

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

import is.Proj2_exercises.data.Player;
import is.Proj2_exercises.data.Team;

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", "Varandas"), new Team("Academica", "Coimbra", "Roxo"), 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();
 }

}

Naturally, we must set the database and make it ready to receive the data from our program.
mysql> create database PlayersAndTeams;

Query OK, 1 row affected (0,00 sec)

mysql> use PlayersAndTeams
Database changed
mysql> show tables;
Empty set (0,00 sec)                                   

Once we run the program, we get the following result:
mysql> show tables;
+---------------------------+
| Tables_in_playersandteams |
+---------------------------+
| hibernate_sequence        |
| Player                    |
| Team                      |
+---------------------------+
3 rows in set (0,00 sec)                               

Looking for the contents of the Player table:

mysql> select * from Player;
+------------+--------+----+----------+---------+
| birth      | height | id | name     | team_id |
+------------+--------+----+----------+---------+
| 1987-04-23 |   1.87 |  5 | Albino   |       1 |
| 1987-04-11 |   1.81 |  6 | Bernardo |       1 |
| 1983-05-12 |   1.74 |  7 | Cesar    |       1 |
| 1992-12-03 |   1.67 |  8 | Dionisio |       1 |
| 1985-08-31 |   1.89 |  9 | Eduardo  |       1 |
| 1989-01-06 |   1.95 | 10 | Franco   |       2 |
| 1986-12-07 |    1.8 | 11 | Gil      |       2 |
| 1987-05-14 |   1.81 | 12 | Helder   |       2 |
| 1991-06-13 |   1.82 | 13 | Ilidio   |       2 |
| 1993-02-04 |   1.83 | 14 | Jacare   |       2 |
| 1984-10-04 |   1.81 | 15 | Leandro  |       3 |
| 1984-06-03 |    1.8 | 16 | Mauricio |       3 |
| 1985-03-11 |   1.88 | 17 | Nilton   |       3 |
| 1990-11-23 |   1.74 | 18 | Oseias   |       3 |
| 1986-09-14 |   1.75 | 19 | Paulino  |       3 |
| 1987-10-10 |   1.77 | 20 | Quevedo  |       3 |
| 1991-07-07 |   1.71 | 21 | Renato   |       4 |
| 1992-07-13 |   1.86 | 22 | Saul     |       4 |
| 1981-01-04 |   1.88 | 23 | Telmo    |       4 |
| 1988-08-29 |   1.84 | 24 | Ulisses  |       4 |
| 1988-05-16 |   1.83 | 25 | Vasco    |       4 |
| 1990-12-08 |   1.82 | 26 | X        |       4 |
| 1987-05-13 |   1.93 | 27 | Ze       |       4 |
+------------+--------+----+----------+---------+
23 rows in set (0,00 sec)

The Team table also corresponds to what we added in the program:

mysql> select * from Team;
+----------+----+-----------+---------------+
| address  | id | name      | presidentname |
+----------+----+-----------+---------------+
| Alvalade |  1 | Sporting  | Varandas      |
| Coimbra  |  2 | Academica | Roxo          |
| Antas    |  3 | Porto     | Costa         |
| Luz      |  4 | Benfica   | Vieira        |
+----------+----+-----------+---------------+
4 rows in set (0,00 sec)


Now, we solve the exercise that asks for players taller than some height:

package is.Proj2_exercises;

import java.util.List;

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

import is.Proj2_exercises.data.Player;

public class ReadData1 {

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

}

and we get the following result:
Albino id = 5, 1.87 plays for Sporting. Born on 1987-04-23
Eduardo id = 9, 1.89 plays for Sporting. Born on 1985-08-31
Franco id = 10, 1.95 plays for Academica. Born on 1989-01-06
Nilton id = 17, 1.88 plays for Porto. Born on 1985-03-11
Saul id = 22, 1.86 plays for Benfica. Born on 1992-07-13
Telmo id = 23, 1.88 plays for Benfica. Born on 1981-01-04
Ze id = 27, 1.93 plays for Benfica. Born on 1987-05-13

Let us try to see if we can list the players from our favorite team. The interesting thing to note in this solution is that I am taking advantage of the Object-relational-mapping to pick the team from the database and then I just explore the players field from the Team object. Rather simple no?

package is.Proj2_exercises;

import java.util.List;

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

import is.Proj2_exercises.data.Player;
import is.Proj2_exercises.data.Team;

public class ReadData2 {

 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.getName());
   }
 }

}

The result is the following:

Franco
Gil
Helder
Ilidio
Jacare