I'm using datanucleus 3.2.7 from Maven, trying to use the Amazon S3 JPA provider. I can successfully write data into S3, but querying either by using "SELECT u FROM User u" or "SELECT u FROM User u WHERE id = :id" causes a NullPointerException to be thrown when I call query.getResultList().
Using the RDBMS provider, everything works perfectly. Is there something I'm doing wrong?
Main.java
EntityManagerFactory factory = Persistence.createEntityManagerFactory("MyUnit");
EntityManager entityManager = factory.createEntityManager();
Query query = entityManager.createQuery("SELECT u FROM User u", User.class);
List<User> users = query.getResultList(); // Null pointer exception here (This is line 16!)
for(User u:users)
System.out.println(u);
User.java
package test;
import javax.persistence.*;
#Entity
#Table(name = "User")
public class User {
#Id
public String id;
public String name;
public User(String id, String name) {
this.id = id;
this.name = name;
}
public String toString() {
return id+" : "+name;
}
}
persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="MyUnit">
<class>test.User</class>
<exclude-unlisted-classes />
<properties>
<properties>
<property name="datanucleus.ConnectionURL" value="amazons3:http://s3.amazonaws.com/" />
<property name="datanucleus.ConnectionUserName" value="xxxxx" />
<property name="datanucleus.ConnectionPassword" value="xxxxx" />
<property name="datanucleus.cloud.storage.bucket" value="my-bucket" />
</properties>
<property name="datanucleus.autoCreateSchema" value="true" />
</properties>
</persistence-unit>
</persistence>
Exception
java.lang.NullPointerException
at org.datanucleus.NucleusContext.isClassWithIdentityCacheable(NucleusContext.java:1840)
at org.datanucleus.ExecutionContextImpl.getObjectFromLevel2Cache(ExecutionContextImpl.java:5287)
at org.datanucleus.ExecutionContextImpl.getObjectFromCache(ExecutionContextImpl.java:5191)
at org.datanucleus.ExecutionContextImpl.findObject(ExecutionContextImpl.java:3137)
at org.datanucleus.store.json.CloudStoragePersistenceHandler.getObjectsOfCandidateType(CloudStoragePersistenceHandler.java:367)
at org.datanucleus.store.json.query.JPQLQuery.performExecute(JPQLQuery.java:94)
at org.datanucleus.store.query.Query.executeQuery(Query.java:1786)
at org.datanucleus.store.query.Query.executeWithMap(Query.java:1690)
at org.datanucleus.api.jpa.JPAQuery.getResultList(JPAQuery.java:194)
at test.Main.main(Main.java:16)
This error appears to be happening as datanucleus deserializes the JSON for an entry. Deleting everything from the bucket returns the empty set without incident. By turning off L2 Caching, I made the exception occur somewhere else. It seems that ExecutionContextImpl.findObject is being given a null id.
datanucleus-json version 3.2.1 likely fixes that, but that's for you to confirm
Related
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
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.
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>
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>
The #Table annotation has a "schema" element to configure the schema at the Entity level
#Table(name = "Users", schema = "Sankofa")
I hava a model, and i need to validate model with eclipselink jpa
and i add anotation #NotEmpty validate name if empty, but when i save/persist model, validate not work
#Entity
public class Role {
#Id
private String id;
#NotEmpty
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
my jpa configuration xml like this
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="Eclipselink_JPA" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>app.test.Model.Role</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
</properties>
</persistence-unit>
</persistence>
The JPA API (which EclipseLink implements) is nothing to do with the Bean Validation API.
To utilise the Bean Validation API you need the Bean Validation API jar (javax.validation), together with an implementation of that API (Apache BVAL, Hibernate Validator, etc) in your CLASSPATH.
The only thing JPA provides is to auto-enable validation as per this link, but the default is "auto" so nothing required really for that
i want to query a mongodb here my code
Persistence.xml
<?xml version="1.0"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="primary" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
<properties>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/>
<property name="hibernate.ogm.datastore.provider" value="mongodb" />
<property name="hibernate.ogm.datastore.database" value="******" />
<property name="hibernate.ogm.datastore.host" value="******" />
<property name="hibernate.ogm.datastore.port" value="******" />
<property name="hibernate.ogm.datastore.username" value="******" />
<property name="hibernate.ogm.datastore.password" value="******" />
</properties>
</persistence-unit>
</persistence>
Flux.java
#Entity
#Table(catalog="f12", schema="public", name="enl_flux_f12_entry")
public class enl_flux_f12_entry{
#Id
public String id;
public String SYS_FluxName;
public byte[] SYS_ReadDateTime;
public String SYS_BaseNameZip;
public Long SYS_Status;
public String SYS_DateCreaERDF;
}
main
public static void main(String[] args) throws ClassNotFoundException{
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "primary" );
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
enl_flux_f12_entry f = entityManager.find(Flux.class, "*id*");
System.out.println(f.id);
entityManager.flush();
entityManager.close();
}
mongodb
{
"_id" : ObjectId("rzerzer"),
"SYS_FluxName" : "zerzerze.xml",
"SYS_ReadDateTime" : Timestamp(6300883749567463, 83),
"SYS_BaseNameZip" : "rferfer.zip",
"SYS_Status" : NumberLong(1),
"SYS_DateCreaERDF" : "2016-03-01T20:38:48Z"
}
The problem is that entityManager.find return always null. Is there any problem in my code?
I think it returns null because something odd in the mapping or in the JSON object and it cannot find the entity you are looking for.
The JSON object you want to get has _id: ObjectId("rzerzer"), this doesn't look right because an ObjectId in MongoDB should be:
The 12-byte ObjectId value consists of:
a 4-byte value representing the seconds since the Unix epoch,
a 3-byte machine identifier,
a 2-byte process id, and
a 3-byte counter, starting with a random value.
Even if the object in the DB is right, it is mapped as a String, so Hibernate OGM does not expect an ObjectId.
The mapping of the id on the entity should be:
#Id
#Type(type = "objectid")
public String id;
or
#Id
public ObjectId id;
Another strange thing is the way you are using find:
enl_flux_f12_entry f = entityManager.find(Flux.class, "*id*");
the find method requires the exact id of the entity. If the mapping is right, this should work entityManager.find(Flux.class, "rzerzer");
If you are not sure about the id value in the db you can also use HQL:
List<Flux> entries = entityManager.createQuery( "from Flux" ).list();
i'm trying to make a simple multitenant example to run, using Eclipselink 2.5.2, and MySQL.
When trying to persist an entity asigned to a tenant id, mysql server throws an error: "Table 'jpatest.tenant1_userdata' doesn't exist". (userdata being the entity, jpatest the database name, and tenant1 the tenant-id)
The table indeed doesn't exist, the database jpatest do exist. I was expecting eclipselink to autogenerate the tables each time i try to persist with a new tenant id.
So the question would be:
How can i force Eclipselink to create the tables?
If that is not possible; How can i create tables at runtime?
Here's the code:
Entity:
#Entity
#Table(name = "userdata")
#Multitenant(value = MultitenantType.TABLE_PER_TENANT)
#TenantTableDiscriminator(type = TenantTableDiscriminatorType.PREFIX, contextProperty = "tenant-id")
public class UserData implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private static final long serialVersionUID = 1L;
private String name;
.
.
.
persistence.xml
<?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="MultiTeanantTest" transaction-type="RESOURCE_LOCAL">
<class>UserData</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpatest" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="javax.persistence.schema-generation.create-database-schemas" value="true"/>
<property name="eclipselink.ddl-generation" value="create-tables"/>
</properties>
</persistence-unit>
</persistence>
Main class:
public class Main {
public static void main(String[] args) {
UserData ud = new UserData();
ud.setNombre("John);
Map properties = new HashMap<>();
properties.put("tenant-id", "tenant1");
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MultiTeanantTest", properties );
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(ud);
em.getTransaction().commit();
}
}
Hope someone can give me a tip in what i'm doing wrong.
DDL generation will not be supported in a Multitenant Scenario by Eclipselink.
Refer to this link for more information, https://wiki.eclipse.org/EclipseLink/DesignDocs/Multi-Tenancy/TablePerTenant
I'm working on an web application using Hibernate 4, Spring 3. I'm struggling with an issue for days.
In the application I have an entity class named 'User':
#Entity
public class User {
#Id
private int uid;
#Column(unique = true)
private String username;
private String password;
private boolean confirmed;
//... getters and setters
}
This entity class is mapped from a table of db called 'user'.
There is another entity class named 'Confirmation':
#Entity
#NamedQueries({
#NamedQuery(name = "getAllConfirmations", query = "select c from Confirmation c")
})
public class Confirmation {
#Id
private String username;
private boolean confirmed;
//... getters and setters
}
This entity class is mapped from a view of db called 'confirmation'.
create view confirmation
as
select username,confirmed from user
I have a method in my service class to get list of all Confirmation object:
public List<Confirmation> getListOfConfirmations() {
Query query = entityManager.createNamedQuery("getAllConfirmations");
return query.getResultList();
}
Also, there's a method to confirm User objects- set confirmed field 'true':
public int confirmUser(int uid) {
User user = getUser(uid);
if (user != null) {
user.setConfirmed(true);
try {
entityManager.getTransaction().begin();
entityManager.merge(user);
entityManager.getTransaction().commit();
return 1;
} catch (Exception e) {
e.printStackTrace();
}
}
return 0;
}
When I call confirmUser() it works fine and User object and it's row in database would be changed, But when I call getListOfConfirmations() after changes no change would be viewed in the result list.
It seems, by default, hibernate caches query result of views.
Is there any way to prevent hibernate to prevent caches this result.
In addition, I've tried #Cacheable(false) for Confirmation class or set #QueryHint(name = "org.hibernate.cacheable", value = "false") for getAllConfirmations named query, these don't work.
Persistence config:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="MyPU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>bob.jpacache.Confirmation</class>
<class>bob.jpacache.User</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost/cachetest"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="root"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
Thanks, in advance, for your help.
#Cacheable refers to the second-level cache and your problem seems to me that it is in the first level cache. I believe that the problem is in the life cycle of your EntityManager (at what time you create a new, at what time you close it), do a simple test: after the change of the User's objects, try call entityManager.clear () and then query your Confirmation object. NOTE: entityManager.clear () must be called on the same EntityManager that will be used to make the query.
Another detail, do not know what his real purpose, but the query has no filter. Thus, whether the User has to confirm or not, will always bring all records of User table.