0

This is a simple guide for configuring JPA and connecting to your database using JTA transaction mode. Also it is including the most common mistakes that developers do and you should avoid them. Hope this help you.

mtz1406
  • 75
  • 1
  • 10

1 Answers1

0

1- Set up a DataSource in your Application Server: In order to configure JPA in your WebApp using JTA mode you need 1st setting up a DataSource. You can setup the DataSource from your Application Server (Glassfish / Payara / ...). but it is recommended to setup the Datasource through your Web App. Follow these steps to setup the DataSource for Glassfish or Payara through your Maven WebApp:

  1. create a new folder (NOT Package) inside your project folder with name "setup".
  2. create a new xml file and name it "glassfish-resources.xml" and save it inside "setup" folder.And write the bellow content:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
    <jdbc-resource enabled="true" jndi-name="**jdbc/DBDev01**" object-type="user" pool-name="**jdbc/DBDev01-ConnectionPool**">
    <description/>
  </jdbc-resource>
   <jdbc-connection-pool allow-non-component-callers="false" 
                        associate-with-thread="false" connection-creation-retry-attempts="0" 
                        connection-creation-retry-interval-in-seconds="10" 
                        connection-leak-reclaim="false" 
                        connection-leak-timeout-in-seconds="0" 
                        connection-validation-method="auto-commit" 
                        datasource-classname="**org.mariadb.jdbc.MariaDbDataSource**" 
                        fail-all-connections="false" 
                        idle-timeout-in-seconds="300" 
                        is-connection-validation-required="false" 
                        is-isolation-level-guaranteed="true" 
                        lazy-connection-association="false" 
                        lazy-connection-enlistment="false" 
                        match-connections="false" 
                        max-connection-usage-count="0" 
                        max-pool-size="32" 
                        max-wait-time-in-millis="60000" 
                        name="**jdbc/DBDev01-ConnectionPool**" 
                        non-transactional-connections="false" 
                        pool-resize-quantity="2" 
                        res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
<!-- for MariaDB users, it recomended to add ?useMysqlMetadata=true, this will make MariaDB pretending that it is a MySQL for tools or libraries that not support MariaDB -->    
<property name="URL" value="**jdbc:mariadb://XXX.XXX.XXX.XXX:XXXX/DB_NAME?useMysqlMetadata=true**"/>
    <property name="User" value="**USERNAME**"/>
    <property name="Password" value="**PASSWORD**"/>
  </jdbc-connection-pool>
</resources>

Note: All values in between ** ** should be modified as per your settings. This file will be loaded by your application server (Glassfish/Payara) after deploying your webapp. For Payara users you can also name the file with "payara-resources.xml" but with little modifications. Ref:Payara Deployment Descriptors.

2- Add Resource reference for the datasource in WEB-INF/web.xml: you need to add a resource reference for the DataSource in your Webapp through adding this in WEB-INF/web.xml file:

<web-app .....>
  ......

<resource-ref>
  <description>**DBDev01**</description>
  <res-ref-name>**jdbc/DBDev01**</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
  <!-- <res-sharing-scope>Shareable</res-sharing-scope> -->
</resource-ref>
</web-app>

Note: The res-ref-name should match exactly the name you choose for the datasource in glassfish resources file.

3- Configure 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="**MyDB**" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <!--  List all Entity classes  -->
    <class>**com.MyEntityClassName**</class>

    <jta-data-source>**jdbc/DBDev01**</jta-data-source>
<!--  you can list all entity classes you need and set this value to true. or set this value to false to include other entity clases  -->
    <exclude-unlisted-classes>true</exclude-unlisted-classes>

<properties>
             <!-- while eclipselink not support MariaDB , set this property to enforce eclipselink to work with it as MySQL -->
            <property name="eclipselink.target-database" value="MySQL"/>
        </properties>
  </persistence-unit>
  </persistence>

Note 1: If you want to use JTA as transaction type, So you must defining jta-data-source. And it is a common mistake for developers adding DB URL,username and password in properties trying to connect to the database without defining the JTA data Source. This will not work and will lead your application server to use the default datasource that already defined which in common is an H2 database.

Note 2: eclipselink (JPA library) not supporting MariaDB. But there is a workarounds for that. solution 1:: add "?useMysqlMetadata=true" as suffix on your connection URL Like: <property name="URL" value="**jdbc:mariadb://XXX.XXX.XXX.XXX:XXXX/DB_NAME?useMysqlMetadata=true"/> this will make MariaDB pretending that it is a MySQL. solution 2: Enforce eclipselink to deal with the database as MySQL. this can be done by setting eclipselink.target-database property in persistence.xml as bellow:

<properties>
         <!-- while eclipselink not support MariaDB , set this property to enforce eclipselink to work with it as MySQL -->
        <property name="eclipselink.target-database" value="MySQL"/>
    </properties>

4- Add JDBC client as Dependency in POM.xml:

<dependency>
<!--  This is for MariaDB. You should change it if you are using other kind of DB like MySQL or Oracle DB  -->
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>2.7.2</version>
</dependency>

5- Enjoy with your Codes: writing a sessionBean:

@Stateless
public class StudentManager
{
/* Notes: 
1.you should use the same name exactly that defined in Persistence.xml file.
2.You can not use @PersistenceUnit with JTA. only use @PersistenceContext with JTA.
*/

    @PersistenceContext(unitName="MyDB")
    private EntityManager em;
    
    public StudentManager()
    {
        
    }
    
    public void persist(Student student) {
        
        em.persist(student);
    }    
}

write TestController:

@Named
@SessionScoped
public class TestController
{
    @Inject
    private StudentManager studentManager;
    private String message = "";
    
    public void test()
    {
        Student student = new Student();
   
        Student.setCode(11223344);
        Student.setName("John");
        
        studentManager.persist(Student); 
/*Note:we used studentManager directly without constructing.
writing studentManager = new StudentManager() is a common mistake and will lead you to get a null EntityManager.*/
        this.setMessage("A new Student already saved successful with Code:" + Student.getCode());
    }

Common Question: Should use @Inject or @EJB? here is the answer

A simple JSF page for testing:

<?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">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
             <h:commandButton value="Test Save a Student over JTA" action="#{testController.test()}" />
             <br />
             <h:outputLabel for="message" value="#{test.message}" />
        </h:form>

    </h:body>
</html>

mtz1406
  • 75
  • 1
  • 10