Skip to main content

8. JSF: Using JPA, connection pool, JNDI Datasources

0. Introduction

I have been googling and found 2 important posts on this matter, they both use Apache DBCP with JPA Hibernate
  1. Boraji.com :  No persistence.xml is used. All programmatically.
  2. Memorynotfound.com : With persistence.xml
  3. JavaHelps: Simplifies the process of using JPA with Hibernate

However, I want to define the data sources in Tomcat as JNI resource.

1. Create a simple web Maven project

The detailed steps to do this is in this post. Basically :

a. Project-New Maven project (check "Create a simple project (skip archetype selection)" and "Use default Workspace location") - Next.

b. Select "WebJPA" for Group Id, Artifact Id and Name. Select Packaging to "war". Optionally provide a Description, Finish.

c. A Maven pom.xml has been created.


d. A properties section is inclosed for detailing java compiler version, encoding, web.xml omitting.

e. A dependencies section for enclosing the needed jars.

   e.1 Servlet Api (javax.servlet-api: 4.0.0)
   e.2 Oracle implementation of JSF 2.3.3 (javax.faces: 2.3.3)
   e.3 Java expresion language evaluation (javax.el-api 3.0.1-b04)
   e.4 Primefaces (primefaces: 6.1)
   e.5 Weld CDI (weld-servlet-shaded: 3.0.1)
   e.6 Project Lombok -setters and getters (lombok: 1.16.18)
   e.7 Java Assist -for bytecode manipulation (javassist: 3.22.0-GA)
   e.8 Hibernate Core (hibernate-core: 5.2.12.Final)
   e.9 Postgres JDBC (postgresql: 42.1.4)
   e.10 Hibernate Bean Validator (hibernate-validator: 6.04)
   e.11 Apache Tomcat dbcp (tomcat-dbcp: 9.0.1)

The final pom.xml is:


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
<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>WebJPA</groupId>
  <artifactId>WebJPA</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>WebJPA</name>

  <properties>
    <failOnMissingWebXml>false</failOnMissingWebXml>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
     
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.0</version>
      <scope>provided</scope>
    </dependency>
    

    <!--  JSF 2.3.3 OracleImplementation -->
    <!-- https://mvnrepository.com/artifact/org.glassfish/javax.faces -->
    <dependency>
      <groupId>org.glassfish</groupId>
      <artifactId>javax.faces</artifactId>
      <version>2.3.3</version>
    </dependency>
    
    <!--  Expression EL evaluation -->
    <!-- https://mvnrepository.com/artifact/javax.el/javax.el-api -->
    <dependency>
      <groupId>javax.el</groupId>
      <artifactId>javax.el-api</artifactId>
      <version>3.0.1-b04</version>
    </dependency>

    <!--  Primefaces -->
    <dependency>
     <groupId>org.primefaces</groupId>
     <artifactId>primefaces</artifactId>
     <version>6.1</version>
    </dependency>
    

    <!-- Weld CDI for Tomcat with all dependencies included (does not fulfill all capabilities !!!) -->
    <dependency>
      <groupId>org.jboss.weld.servlet</groupId>
      <artifactId>weld-servlet-shaded</artifactId>
      <version>3.0.1.Final</version>
    </dependency>
    
       
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.18</version>
      <scope>provided</scope>
    </dependency>
    
    <!-- Javassist required by Hibernate in some computers -->
    <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
    <dependency>
      <groupId>org.javassist</groupId>
      <artifactId>javassist</artifactId>
      <version>3.22.0-GA</version>
    </dependency>
  
  
    <!-- JPA 2.1 Provider -->
    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>5.2.12.Final</version>
    </dependency>
    

    <!--Postgress JDBC 4 -->
    <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>42.1.4</version>
    </dependency>  
     
 
    <!-- Hibernate Bean Validator Optional -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.4.Final</version>
    </dependency>  
    
     
    <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-dbcp -->
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-dbcp</artifactId>
      <version>9.0.1</version>
    </dependency>
     
    
  </dependencies>
</project>


2.  Create the Postgres database

You should have Postgres and PgAdmin installed. For this example, we will create a user "user" with password "password".

Enter PgAdmin, and create a database (for instance mydb)

You can optionally create schemas if you want. But for educationnal purposes I will create myschema


3. Create a persistence.xml

First, we will create a simple persitence.xml file with the minimum configuration to access our Postgres database. Have a look at the information in read color. Hibernate should create the entities in the database (hibernate.hbm2ddl.auto=update) and we should provide the URL to the database and login credentials for accessing the database.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<persistence 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"
             version="2.1">
    
  <persistence-unit name="mydb_psu" transaction-type="RESOURCE_LOCAL"> 
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
      <property name="hibernate.hbm2ddl.auto" value="update" />
      <property name="hibernate.show_sql" value="true" />      

      <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/mydb" />
      <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
      <property name="hibernate.connection.username" value="user" />
      <property name="hibernate.connection.password" value="password" />    
    
    </properties>
  </persistence-unit>
</persistence>


As mentioned in this blog, we will put it in the src/main/resources/META-INF/persistence.xml


4. Create a simple entity (MyEntity.java)



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;



@Entity
@Table(name = "myentity", schema = "util")
@NoArgsConstructor()
public class MyEntity implements Serializable {
 
 private static final long serialVersionUID = 20171201L;
 
 @Getter
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Short id; 
 
 /** attribute that contains the description, unique value*/
 @Size(max = 50)
 @NotNull ()
 @Column(unique = true)
 @Getter @Setter
 private String description;
 
 /** Transient attribute that means that the system should make a log on any JPA operation of this class*/
 @Transient
 @Getter
 private boolean ok = true;

 
 
 /** Setters*/ 
 
 public void setId(Number id) {
  this.id = id.shortValue();
  
 }

}

Now you should go to PgAdmin and create the database "mydb" and the schema "util"

5. Create a Bootstrap class (HibernateBootstrap.java)


This class lets Hibernate create the entity in the DB. Thi class should have a main method that creates an EntityManagerFactory, and then closes the EntityManagerFactory. In this process, the entity(table) will be created in the database.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package bootstrap;

import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

@Named
@SessionScoped
public class HibernateBootstrap implements Serializable  {
 public static void updateSchema() {
  EntityManagerFactory emf = Persistence.createEntityManagerFactory("mydb_psu");
  emf.close();
 }
 
 public static void main(String[] args) {
  updateSchema();
 }

}

This class has been annotated by @Named and @SessionScoped so that it can be used as a bean in a xhtml file.

Up till now our project structure (Maven) is this one:

src/main/java (Java resources folder)
   |--model  (package)
      |--MyEntity.class
   |--bootstrap (package)
      |--HibernateBootstrap.java

src/main/resources
   |--META-INF (folder)
      |--persistence.xml (JPA configuration)


6. Executing HibernateBootStrap as a Java Application

As this class has a main method, it can be executed (Right-Click - Run As - Java Appliction.

Let's see the DDL that generates Hibernate:

create table util.myentity (id  serial not null, description varchar(255), primary key (id))


alter table util.myentity drop constraint UK_oqxmbwpgnxdnw0hi1683s3n9a


alter table util.myentity add constraint UK_oqxmbwpgnxdnw0hi1683s3n9a unique (description)


As we can see the attribute description has the annotation @Size(max=50) and hibernate DDL generation has not taken this size limitation into account and has created the table field of 255 length.

In order to create the table field of a length of 50, this annotation should have been used @Column (length=50). See Stackoverflow.

But when executed in a web environment, this annotation is taken into account, and the DDL sentence is correct, see my posted question in  Stackoverflow again.

But in the next part of this blog, when we create the table using Tomcat JNDI data source, this @Size annotation is used to create correctly the field.


7. Defining a Tomcat JNDI DataSource

The webapp folder that is in
  • Deployed Resources (in the eclipse project view)
  • src/main (in the project folder)
It should be created the META-INF folder into the webapp folder. In the META-INF folder, we will create the context.xml file that will contain the definition of the Datasource that Tomcat will use. Remember this file is located in the project folder into :

src/main/webapp/META-INF/context.xml

(Remember that the persistence.xml should be placed in another META-INF folder located in src/main/resources folder!)

This is the content of the contex.xml file:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
 <Context path="/mydb" docBase="mydb"
        debug="5" reloadable="true" crossContext="true">
 
    
    <Resource 
      name="mydb_rsc"      auth="Container"
      type="javax.sql.DataSource"
      username="user"
      password="paswword"
      maxTotal="50"
      maxIdle="20"
      maxWait="10000"
      driverClassName="org.postgresql.Driver"
      url="jdbc:postgresql://localhost:5432/mydb"/>
    />
      
        
</Context>

AS you can see, we have defined a connection pool and the connection URL and credentials for accessing the database.

The only important name is the resource name "mydb_rsc"


8. Updating persistence.xml to focus the JNDI Datasource

Now, we need to connect to the JNDI DataSource. The persistence.xml should look as follows:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 <persistence 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"
              version="2.1">
    
  <persistence-unit name="mydb_psu" transaction-type="RESOURCE_LOCAL"> 
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <jta-data-source>java:comp/env/mydb_rsc</jta-data-source>
      
    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
      <property name="hibernate.hbm2ddl.auto" value="update" />
      <property name="hibernate.show_sql" value="true" />      
    </properties>

  </persistence-unit>
</persistence>


Note the highlighted line which makes reference to the datasource. The prefix java:comp/env is added to the resource name mydb_rsc.

Now we need to create a xhtml page with a button that points to HibernateBootstrap.updateSchema to order Hibernate to create the entities. But we need to delete now created entities!


9. A simple xhtml

We have only one button, to perform the schema update. Here is create-table-page.xhtml


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:f="http://java.sun.com/jsf/core" 
      xmlns:p="http://primefaces.org/ui">  
    <h:head>  
    </h:head>  
      
    <h:body>  
      <h:form>  
        <p:commandButton value="Create Tables" action="#{hibernateBootstrap.updateSchema}"/>
       </h:form>
    </h:body>  
</html>


10. Completing the project structure as JSF web application

As mentioned in an older post, we have to provide additional files to the webapp folder to fulfil a JSF web application pattern.

1. Create WEB-INF folder in the webapp folder
2. Create an empty file beans.xml into the WEB-INF folder
3. Create the file faces-config.xml with this content:


1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<faces-config 
              xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
              version="2.2">  

</faces-config>



11. Executing the page create-table-page.xhtml on the Server


Rember to delete myentity table in the database!

Place the mouse on the page and Right-click - Run on Server , press the button



and see the generated DDL sentences:


create table util.myentity (id  serial not null, description varchar(50) not null, primary key (id))

alter table util.myentity drop constraint UK_oqxmbwpgnxdnw0hi1683s3n9a


alter table util.myentity add constraint UK_oqxmbwpgnxdnw0hi1683s3n9a unique (description)


As you can see the table has been correctly created and the @Sixe(max=50) annotation has been  used !!

Comments

Post a Comment

Popular posts from this blog

10. JSF How to access my resources files?

Sometimes it is really difficult to access any resources form different Java applications. So let's see some scenarios: 1. Executable jar application in a NO WEB environment. Take into account that this is not a JSF application !!! It is desired to have a property file outside the "jar file" so that we can change properties when needed. For instance, we will place the properties file in the same folder that the "jar file" lies. To access the folder where the jar is, the trick is to access the folder where the compiled classed are placed and scale 3 folders . (Oh! rather strange, if anybody knows a better idea.., he will be welcome). Here is a simple code. The name of the class containing the code is ThisClass, and we have created a Property class that feeds with a file in the same folder that the" jar file" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /** * Gets the folder where resides the executable...

9. JSF: Solving common problems

1. Target Unreachable, identifier [bean] resolved to null When you reference a bean in a xhtml file with "#{bean.method}" and get this error, you should verify that: The bean implements Serializable . The references (javax.servlet-api.4.0.0, javax.faces.2.3.3,  javax.el.api.3.0.1, org.primefaces.6.1) have been selected in your pom.xml You have created the files bean.xml and faces-config.xml in the webapp/WEB-INF folder You have used the correct annotations from CDI  (javax.inject.Named and javax.enterprise.context.SessionScoped, ...) and not ManagedBean or jsf2.2 scopes. 2. Bean declaring a passivating scope must be passivation capable This error causes server not starting and fills up the console with long chained exceptions. It is very annoying. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 SEVERE: A child container failed during start java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to star...

2. Create a Maven JSF project and a simple login page

Updated on Oct-25-2107 0. Introduction Remember to install previously: Java JDK 8 Eclipse Oxygen Lombok  Apache Tomcat 9 1. Create a Maven Project In the top menu select : File - New - Maven Project Press Next Now fill the next form as follows It is important to select: Group id: org.ximodante.jsf  (or another packet you like) Packaging: war Artifact Id and Name: JSFv02 or any name for the project Press Finish 2. The pom.xml file Eclipse complains about the simple generated file. It seems that a web.xml is required if the packaging is selected to war. The simple generated file is  1 2 3 4 5 6 7 8 9 <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> org.ximodante...