Home > database >  JPA: How to specify tables from different DB2 schemas using @NamedQuery. Schema.Table cannot be the
JPA: How to specify tables from different DB2 schemas using @NamedQuery. Schema.Table cannot be the

Time:11-02

I encountering a problem when sending a GET request from Postman to test my endpoints. The same NamedQueries worked before with Derby with just the one Schema, so no need to differentiate then. I have now changed to DB2 running in a local Docker instance and am using Maven to run Open-Liberty Framework to create the endpoints. I am receiving the below error. Do I need to create a orm.xml file with entity-mappings, or do something else to resolve this? I would prefer to do this without more xml files if possible.

Postman:

Error 500: java.lang.NullPointerException: Cannot invoke >"javax.persistence.EntityManager.createNamedQuery(String, java.lang.Class)" because "this.em" is null

Maven:

[INFO] [ERROR ] CWWJP0015E: An error occurred in the org.eclipse.persistence.jpa.PersistenceProvider persistence provider when it attempted to create the container entity manager factory for the jpa-unit persistence unit. The following error occurred: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.7.9.v20210604-2c549e2208): org.eclipse.persistence.exceptions.EntityManagerSetupException

[INFO] Exception Description: Deployment of PersistenceUnit [jpa-unit] failed. Close all factories for this PersistenceUnit.
[INFO] Internal Exception: Exception [EclipseLink-0] (Eclipse Persistence Services - 2.7.9.v20210604-2c549e2208): org.eclipse.persistence.exceptions.JPQLException
[INFO] Exception Description: Problem compiling [SELECT u FROM Sankofa.Users u].
[INFO] [14, 29] 'Sankofa.Users u' cannot be the first declaration of the FROM clause.

UserDao

package dao;

import java.util.List;
import java.util.concurrent.TimeoutException;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.hyperledger.fabric.gateway.ContractException;

import javax.enterprise.context.RequestScoped;
import models.*;

@RequestScoped
public class UserDao {
    
//DB2 Methods
    @PersistenceContext(name = "jpa-unit")
    private EntityManager em;

    public void createUser(Users user){
        em.persist(user);
    }
    
    public Users readUser(int userID){
        return em.find(Users.class, userID);
    }
    //NEED TO DO set return limit to 20
    public List<Users> readAllUsers(){
        return em.createNamedQuery("Users.findAll", Users.class).getResultList();
    }

    public void updateUser(Users user){
        em.merge(user);
    }

    public void deleteUser(Users userID){
        em.remove(userID);
    }

    public List<Users> findUser(String email){
        return em.createNamedQuery("Users.findUser", Users.class)
            .setParameter("email", email)
            .getResultList(); 
    }

    public void createHistory(History hist){
        em.persist(hist);
    }
    //wait this doesnt do anything? 
    public Users readHistory(int userID){
        return em.find(Users.class, userID);
    }

    public List<History> readAllHistory(){
        return em.createNamedQuery("History.findAll", History.class).getResultList();
    }
}

Users

package models;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.persistence.*;
import java.time.LocalDate;

@Entity
@Table(name = "Users")
@NamedQueries({
    @NamedQuery(name = "Users.findAll", query = "SELECT u FROM Users u"),
    @NamedQuery(name = "Users.findUser", query = "SELECT usr FROM Users usr WHERE usr.email = :email")
})
public class Users {

    private static JsonObjectBuilder builder = Json.createObjectBuilder();

    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    @Column(name = "userId")
    private int id;

    @Column(name = "firstName")
    private String firstName;
    @Column(name = "lastName")
    private String lastName;
    @Column(name = "gender")
    private String gender;
    @Column(name = "address")
    private String address;
    @Column(name = "email")
    private String email;
    @Column(name = "password")
    private String password;
    @Column(name = "dateOfBirth")
    private LocalDate dateOfBirth;

Persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
    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_2.xsd">
    <persistence-unit name="jpa-unit" transaction-type="JTA">
        <mapping-file>orm.xml</mapping-file>
        
        <properties>
            <!-- Connection Specific -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.DB2Dialect"/>
            
            <property name="javax.persistence.schema-generation.database.action" value="create" />
            <property name="javax.persistence.schema-generation.create-database-schemas" value="true" />
            <property name="javax.persistence.schema-generation.scripts.action" value="create" />
            <property name="javax.persistence.schema-generation.scripts.create-target" value="create.ddl"/>

        </properties>
    </persistence-unit>
</persistence>

Server.xml

<server description="Obdoblock REST Server">
    <featureManager>
        <feature>jaxrs-2.1</feature>
        <feature>openapi-3.1</feature>
        <feature>jpa-2.2</feature>
        <feature>cdi-2.0</feature>
    </featureManager>
  
    <httpEndpoint 
        httpPort="${default.http.port}" 
        httpsPort="${default.https.port}"
        id="defaultHttpEndpoint" 
        host="*" 
    />

    <webApplication 
        location="hyperledger-api.war" 
        contextRoot="${app.context.root}"
    />
    
    <!-- DB2 Library Configuration -->
    <library id="DB2JCCLib">
        <file name="${shared.resource.dir}/jcc-11.5.6.0" />
    </library>
    
    
    <dataSource id="DefaultDataSource" jndiName="jdbc/db2">
        <jdbcDriver libraryRef="jdbcLib"/>
        <properties.db2.jcc
            databaseName="testdb"
            serverName="localhost" 
            portNumber="50000"
            user="****" password="****"
        />
    </dataSource>
</server>

Versions:

  • Docker: 20.10.8, build 3967b7d
  • DB2: ibm/db2 docker image version 11.5.6
  • Maven: 3.8.3
  • Java: JDK 14.0.2

If needing any more details, I'm happy to provide them. Thanks, Dylan

CodePudding user response:

How to specify tables from different DB2 schemas using @NamedQuery

AFAIK, you cannot configure the schema value at the query level. All the named queries defined under the Entity at expected to be executed against the same schema.

  1. The database schema can be set at the persistence-unit level via orm.xml mapping file:

    orm.xml

    <entity-mappings ... >
         <persistence-unit-metadata>
             <persistence-unit-defaults>
                 <schema>Sankofa</schema>
             </persistence-unit-defaults>
         </persistence-unit-metadata>   
         . . .
     </entity-mappings>
    

    persistence.xml

    <persistence ... >
        <persistence-unit name="foo">
            <mapping-file>orm.xml</mapping-file>
        </persistence-unit>
    </persistence>
    
  2. For EclipseLink, you can configure a SessionCustomizer class

    public class FooSessionCustomizer 
         implements org.eclipse.persistence.config.SessionCustomizer {
    
         @Override
         public void customize(Session session) throws Exception {
             session.getLogin().setTableQualifier("Sankofa");
         }
     }
    

    persistence.xml

    <persistence ... >
        <persistence-unit name="foo">
            <properties>
                <property name="eclipselink.session.customizer" value="foo.customizer.FooSessionCustomizer" />
            </properties>
        </persistence-unit>
    </persistence>
    
  3. The @Table annotation has a "schema" element to configure the schema at the Entity level

    @Table(name = "Users", schema = "Sankofa")
    
  • Related