JPA - Increment a numeric field through a sequence programmatically - java

I have a JPA 2 web application (Struts 2, Hibernate 4 as JPA implementation only).
The current requirement is to add a (non-id) numeric sequential field, filled for certain rows only, to an existing entity. When inserting a new row, based on a certain condition, I need to set the new field to its highest value + 1 or to NULL.
For example:
ID NEW_FIELD DESCRIPTION
--------------------------------
1 1 bla bla
2 bla bla <--- unmatched: not needed here
3 bla bla <--- unmatched: not needed here
4 2 bla bla
5 3 bla bla
6 4 bla bla
7 bla bla <--- unmatched: not needed here
8 5 bla bla
9 bla bla <--- unmatched: not needed here
10 6 bla bla
In the good old SQL, it would be something like:
INSERT INTO myTable (
id,
new_field,
description
) VALUES (
myIdSequence.nextVal,
(CASE myCondition
WHEN true
THEN myNewFieldSequence.nextVal
ELSE NULL
END),
'Lorem Ipsum and so on....'
)
But I've no clue on how to achieve it with JPA 2.
I know I can define callbacks methods, but JSR-000317 Persistence Specification for Eval 2.0 Eval discourages some specific operations from inside it:
3.5 Entity Listeners and Callback Methods
- Lifecycle callbacks can invoke JNDI, JDBC, JMS, and enterprise beans.
- In general, the lifecycle method of a portable application should not invoke EntityManager or Query operations, access other entity
instances, or modify relationships within the same persistence
context.[43] A lifecycle callback method may modify the
non-relationship state of the entity on which it is invoked.
[43] The semantics of such operations may be standardized
in a future release of this specification.
Summarizing, yes to JDBC (!) and EJB, no to EntityManager and other Entities.
EDIT
I'm trying to achieve the solution described in the answer from #anttix, but I'm encoutering some problem, so please correct me where I'm wrong.
Table
MyTable
-------------------------
ID number (PK)
NEW_FIELD number
DESCRIPTION text
Main Entity
#Entity
#Table(name="MyTable")
public class MyEntity implements Serializable {
#Id
#SequenceGenerator(name="seq_id", sequenceName="seq_id", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_id")
private Long id;
#OneToOne(cascade= CascadeType.PERSIST)
private FooSequence newField;
private String description
/* Getters and Setters */
}
Sub entity
#Entity
public class FooSequence {
#Id
#SequenceGenerator(name="seq_foo", sequenceName="seq_foo", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_foo")
private Long value;
/* Getter and Setter */
}
DAO
myEntity.setNewField(new FooSequence());
entityManager.persist(myEntity);
Exception
Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
[...]
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: ERROR: relation "new_field" does not exist
[...]
Caused by: org.hibernate.exception.SQLGrammarException: ERROR: relation "new_field" does not exist
[...]
Caused by: org.postgresql.util.PSQLException: ERROR: relation "new_field" does not exist
What am I doing wrong ? I'm pretty new to JPA 2 and I've never used an entity not associated to a physical table... this approach is totally new to me.
I guess I need to put the #Column definition somewhere: how could JPA possibly know that the newField column (mapped through ImprovedNamingStrategy to new_field on the database) is retrieved through the value property of the FooSequence entity ?
Some pieces of the puzzle are missing.
EDIT
As asked in comments, this is the persistence.xml:
<?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="MyService" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/myDS</jta-data-source>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.ejb.naming_strategy"
value="org.hibernate.cfg.ImprovedNamingStrategy"/>
<property name="hibernate.query.substitutions"
value="true 'Y', false 'N'"/>
<property name="hibernate.show_sql" value="true" />
<property name="format_sql" value="true" />
<property name="use_sql_comments" value="true" />
</properties>
</persistence-unit>
</persistence>

One possible solution is to use a separate entity with its own table that will encapsulate only the new field and have an OneToOne mapping with that entity. You will then instantiate the new entity only when you encounter an object that needs the additional sequence number. You can then use any generator strategy to populate it.
#Entity
public class FooSequence {
#Id
#GeneratedValue(...)
private Long value;
}
#Entity
public class Whatever {
#OneToOne(...)
private FooSequnce newColumn;
}
See:
Hibernate JPA Sequence (non-Id)
https://forum.hibernate.org/viewtopic.php?p=2405140
A gradle 1.11 runnable SSCCE (using Spring Boot):
src/main/java/JpaMultikeyDemo.java
import java.util.List;
import javax.persistence.*;
import lombok.Data;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
#Configuration
#EnableTransactionManagement
#EnableAutoConfiguration
public class JpaMultikeyDemo {
#Entity #Data
public static class FooSequence {
#Id #GeneratedValue private Long value;
}
#Entity #Data
public static class FooEntity {
#Id #GeneratedValue private Long id;
#OneToOne private FooSequence sequence;
}
#PersistenceContext
EntityManager em;
#Transactional
public void runInserts() {
// Create ten objects, half with a sequence value
for(int i = 0; i < 10; i++) {
FooEntity e1 = new FooEntity();
if(i % 2 == 0) {
FooSequence s1 = new FooSequence();
em.persist(s1);
e1.setSequence(s1);
}
em.persist(e1);
}
}
public void showAll() {
String q = "SELECT e FROM JpaMultikeyDemo$FooEntity e";
for(FooEntity e: em.createQuery(q, FooEntity.class).getResultList())
System.out.println(e);
}
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(JpaMultikeyDemo.class);
context.getBean(JpaMultikeyDemo.class).runInserts();
context.getBean(JpaMultikeyDemo.class).showAll();
context.close();
}
}
build.gradle
apply plugin: 'java'
defaultTasks 'execute'
repositories {
mavenCentral()
maven { url "http://repo.spring.io/libs-milestone" }
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-data-jpa:1.0.0.RC5"
compile "org.projectlombok:lombok:1.12.6"
compile "com.h2database:h2:1.3.175"
}
task execute(type:JavaExec) {
main = "JpaMultikeyDemo"
classpath = sourceSets.main.runtimeClasspath
}
See also: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-configure-datasource

This looks like it could be a good case for some AOP. First start by creating a custom field annotation #CustomSequenceGeneratedValue, and then annotate the field on the entity with it:
public class MyEntity {
...
#CustomSequenceGeneratedValue
private Long generatedValue;
public void setGeneratedValue(long generatedValue) {
}
}
Then an aspect is created to increment generated values:
#Aspect
public class CustomSequenceGeneratedValueAspect {
#PersistenceContext
private EntityManager em;
#Before("execution(* com.yourpackage.dao.SomeDao.*.*(..))")
public void beforeSaving(JoinPoint jp) throws Throwable {
Object[] args = jp.getArgs();
MethodSignature ms = (MethodSignature) jp.getSignature();
Method m = ms.getMethod();
Annotation[][] parameterAnnotations = m.getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {
Annotation[] annotations = parameterAnnotations[i];
for (Annotation annotation : annotations) {
if (annotation.annotationType() == CustomSequenceGeneratedEntity.class) {
... find generated properties run query and call setter ...
... Query query = em.createNativeQuery("select MY_SEQUENCE.NEXTVAL from dual");
}
}
}
}
}
Then the aspect is scanned with <aop:aspectj-autoproxy />, and applied to any Spring DAO saving entities of this type. The aspect would populate the sequence generated values based on a sequence, in a transparent way for the user.

You mentioned being open to using JDBC. Here is how you can you use Entity Callback with JdbcTemplate, the example uses Postgres's syntax for selecting next value in a sequence, just update it to use the right syntax for your DB.
Add this to your entity class:
#javax.persistence.EntityListeners(com.example.MyEntityListener.class)
And here is listener implementation (#Qualifier and required = true are necessary for it to work):
package com.example;
import javax.persistence.PostPersist;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
#Component
public class MyEntityListener {
private static JdbcTemplate jdbcTemplate;
#Autowired(required = true)
#Qualifier("jdbcTemplate")
public void setJdbcTemplate(JdbcTemplate bean) {
jdbcTemplate = bean;
}
#PostPersist
#Transactional
public void postPersis(MyEntity entity) {
if(isUpdateNeeded(entity)) {
entity.setMyField(jdbcTemplate.queryForObject("select nextval('not_hibernate_sequence')", Long.class));
}
}
private boolean isUpdateNeeded(MyEntity entity) {
// TODO - implement logic to determine whether to do an update
return false;
}
}

The hacky solution I used to keep it simple is the following:
MyEntity myEntity = new MyEntity();
myEntity.setDescription("blabla");
em.persist(myEntity);
em.flush(myEntity);
myEntity.setNewField(getFooSequence());
The complete code ("pseudo-code", I've written it directly on SO so it could have typos) with transaction handling would be like :
Entity
#Entity
#Table(name="MyTable")
public class MyEntity implements Serializable {
#Id
#SequenceGenerator(name="seq_id", sequenceName="seq_id", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_id")
private Long id;
private Long newField; // the optional sequence
private String description
/* Getters and Setters */
}
Main EJB:
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER) // default
public class MainEjb implements MainEjbLocalInterface {
#Inject
DaoEjbLocalInterface dao;
// Create new session, no OSIV here
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public Long insertMyEntity(boolean myCondition) throws Exception {
try {
MyEntity myEntity = dao.insertMyEntity();
// if this break, no FooSequence will be generated
doOtherStuff();
// Do other non-database stuff that can break here.
// If they break, no FooSequence will be generated,
// and no myEntity will be persisted.
if (myCondition) {
myEntity.setNewField(dao.getFooSequence());
// This can't break (it would have break before).
// But even if it breaks, no FooSequence will be generated,
// and no myEntity will be persisted.
}
} catch (Exception e){
getContext().setRollbackOnly();
log.error(e.getMessage(),e);
throw new MyException(e);
}
}
}
DAO EJB
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER) // default
public class DaoEjb implements DaoEjbLocalInterface {
#PersistenceContext( unitName="myPersistenceUnit")
EntityManager em;
// default, use caller (MainEJB) session
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public MyEntity insertMyEntity() throws Exception{
MyEntity myEntity = new MyEntity();
myEntity.setDescription("blabla");
em.persist(myEntity);
em.flush(); // here it will break in case of database errors,
// eg. description value too long for the column.
// Not yet committed, but already "tested".
return myEntity;
}
// default, use caller (MainEJB) session
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public Long getFooSequence() throws Exception {
Query query = em.createNativeQuery("SELECT nextval('seq_foo')");
return ((BigInteger) query.getResultList().get(0)).longValue();
}
}
This will guarantee there will be no gaps in the FooSequence generation.
The only drawback, that I don't care at all in my use case, is that FooSequence and the #Id sequence are not synchronized, so two concurrent inserts may have "inverted" FooSequence values, respecto to their order of arrive, eg.
ID NEW FIELD
-------------
1 2
2 1

Related

#Version field in entity class not increased during JPA test even though I updated it

I have a simple Repository:
public interface ReviewRepository extends CrudRepository<ReviewEntity, Integer> {
#Transactional(readOnly = true)
List<ReviewEntity> findByProductId(int productId);
}
I want to test it using test containers I followed the procedures and wrote my test case:
public abstract class MySqlTestBase {
private static MySQLContainer database = new MySQLContainer("mysql:5.7.32");
static {
database.start();
}
#DynamicPropertySource
static void databaseProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", database::getJdbcUrl);
registry.add("spring.datasource.username", database::getUsername);
registry.add("spring.datasource.password", database::getPassword);
}
}
#DataJpaTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class PersistTests extends MySqlTestBase {
#Autowired
private ReviewRepository repository;
private ReviewEntity savedEntity;
#BeforeEach
void setupDb() {
repository.deleteAll();
ReviewEntity entity = new ReviewEntity(1, 2, "author1");
savedEntity = repository.save(entity);
assertEqualsReview(entity, savedEntity);
}
#Test
void update() {
savedEntity.setAuthor("author2");
repository.save(savedEntity);
ReviewEntity foundEntity = repository.findById(savedEntity.getId()).get();
assertEquals(1, (long)foundEntity.getVersion());
assertEquals("author2", foundEntity.getAuthor());
}
}
my ReviewEntity also is written like:
#Entity
public class ReviewEntity {
#Id #GeneratedValue
private int id;
#Version
private int version;
private int productId;
private int reviewId;
private String author;
public ReviewEntity(int productId, int reviewId, String author) {
this.productId = productId;
this.reviewId = reviewId;
this.author = author;
}
// setter and getter
}
When I run this test it fails at the assertEquals(1, (long)foundEntity.getVersion()); line with this message:
expected: <1> but was: <0>
Expected :1
Actual :0
But I update the ReviewEntity class and according to the documentation the #Version field should automatically increases but this not happens. what part of my test is wrong?
If you look at the default implementation of save method in CrudRepository interface in the SimpleJpaRepository class you will see save method is implemented like:
#Transactional
#Override
public <S extends T> List<S> saveAll(Iterable<S> entities) {
Assert.notNull(entities, "Entities must not be null!");
List<S> result = new ArrayList<S>();
for (S entity : entities) {
result.add(save(entity));
}
return result;
}
meaning it is marked with #Transactional with Required as its propagation level(it is default)
Required propagation works like this:
REQUIRED is the default propagation. Spring checks if there is an
active transaction, and if nothing exists, it creates a new one.
Otherwise, the business logic appends to the currently active
transaction
and for DataJpaTest annotation comment section says:
By default, tests annotated with #DataJpaTest are transactional and
roll back at the end of each test
So for method update in your test a transaction is going to be created and the save method in repository.save(savedEntity); is going to be appended to that transaction. meaning it is committed only if that transaction successfully committed and we now know that's not going to happen.
A workaround for this problem probably would be to annotate test class with #Transactional(propagation = NOT_SUPPORTED) to suspends the currently running transaction then for repository.save(savedEntity); a transaction is going to be created and committed at the end of save method and then you can proceed in your test.

Spring boot does not throw 409 for duplicate entity in database. What should be the response? [duplicate]

I'm currently playing around on Spring boot 1.4.2 in which I've pulled in Spring-boot-starter-web and Spring-boot-starter-jpa.
My main issue is that when I save a new entity it works fine (all cool).
However if I save a new product entity with the same id (eg a duplicate entry), it does not throw an exception. I was expecting ConstrintViolationException or something similar.
Given the following set up:
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
ProductRepository.java
#Repository
public interface ProductRepository extends JpaRepository<Product, String> {}
JpaConfig.java
#Configuration
#EnableJpaRepositories(basePackages = "com.verric.jpa.repository" )
#EntityScan(basePackageClasses ="com.verric.jpa")
#EnableTransactionManagement
public class JpaConfig {
#Bean
JpaTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}
Note JpaConfig.java and Application.java are in the same package.
ProductController.java
#RestController
#RequestMapping(path = "/product")
public class ProductController {
#Autowired
ProductRepository productRepository;
#PostMapping("createProduct")
public void handle(#RequestBody #Valid CreateProductRequest request) {
Product product = new Product(request.getId(), request.getName(), request.getPrice(), request.isTaxable());
try {
productRepository.save(product);
} catch (DataAccessException ex) {
System.out.println(ex.getCause().getMessage());
}
}
}
and finally Product.java
#Entity(name = "product")
#Getter
#Setter
#AllArgsConstructor
#EqualsAndHashCode(of = "id")
public class Product {
protected Product() { /* jpa constructor*/ }
#Id
private String id;
#Column
private String name;
#Column
private Long price;
#Column
private Boolean taxable;
}
The getter, setter and equalsHashcode.. are lombok annotations.
Miscellaneous:
Spring boot : 1.4.2
Hibernate ORM: 5.2.2.FINAL
This issue happens regardless if I annotate the controller with or without #Transactional
The underlying db shows the exception clearly
2016-11-15 18:03:49 AEDT [40794-1] verric#stuff ERROR: duplicate key value violates unique constraint "product_pkey"
2016-11-15 18:03:49 AEDT [40794-2] verric#stuff DETAIL: Key (id)=(test001) already exists
I know that is better (more common) to break the data access stuff into its own service layer instead of dumping it in the controller
The semantics of the controller aren't ReST
Things I've tried:
Spring CrudRepository exceptions
I've tried implementing the answer from this question, unfortunately my code never ever hits the DataAccesException exception
Does Spring JPA throw an error if save function is unsuccessful?
Again similar response to the question above.
http://www.baeldung.com/spring-dataIntegrityviolationexception
I tried adding the bean to my JPAconfig.java class that is:
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
But nothing seemed to happen.
Sorry for long post, ty in advance
My solution is a lot cleaner. Spring Data already provides a nice way for us to define how an entity is considered to be new. This can easily be done by implementing Persistable on our entities, as documented in the reference.
In my case, as is the OP's, the IDs come from an external source and cannot be auto generated. So the default logic used by Spring Data to consider an entity as new if the ID is null wouldn't have worked.
#Entity
public class MyEntity implements Persistable<UUID> {
#Id
private UUID id;
#Transient
private boolean update;
#Override
public UUID getId() {
return this.id;
}
public void setId(UUID id) {
this.id = id;
}
public boolean isUpdate() {
return this.update;
}
public void setUpdate(boolean update) {
this.update = update;
}
#Override
public boolean isNew() {
return !this.update;
}
#PrePersist
#PostLoad
void markUpdated() {
this.update = true;
}
}
Here, I have provided a mechanism for the entity to express whether it considers itself new or not by means of another transient boolean property called update. As the default value of update will be false, all entities of this type are considered new and will result in a DataIntegrityViolationException being thrown when you attempt to call repository.save(entity) with the same ID.
If you do wish to perform a merge, you can always set the update property to true before attempting a save. Of course, if your use case never requires you to update entities, you can always return true from the isNew method and get rid of the update field.
The advantages of this approach over checking whether an entity with the same ID already exists in the database before saving are many:
Avoids an extra round trip to the database
We cannot guarantee that by the time one thread has determined that this entity doesn't exist and is about to persist, another thread doesn't attempt to do the same and result in inconsistent data.
Better performance as a result of 1 and having to avoid expensive locking mechanisms.
Atomic
Simple
EDIT: Don't forget to implement a method using JPA callbacks that sets the correct state of the update boolean field just before persisting and just after loading from the database. If you forget to do this, calling deleteAll on the JPA repository will have no effect as I painfully found out. This is because the Spring Data implementation of deleteAll now checks if the entity is new before performing the delete. If your isNew method returns true, the entity will never be considered for deletion.
I think you are aware of CrudRepository.save() is used for both insert and update. If an Id is non existing then it will considered an insert if Id is existing it will be considered update. You may get an Exception if your send the Id as null.
Since you don't have any other annotations apart from #Id on your id variable, The Unique Id generation must be handled by your code Or else you need to make use of #GeneratedValue annotation.
To build upon Shazins answer and to clarify. the CrudRepositroy.save() or JpaRespository.saveAndFlush() both delegate to the following method
SimpleJpaRepository.java
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
Hence if a user tries to create a new entity that so happens to have the same id as an existing entity Spring data will just update that entity.
To achieve what I originally wanted the only thing I could find was to drop back down to JPA solely, that is
#Transactional
#PostMapping("/createProduct")
public Product createProduct(#RequestBody #Valid Product product) {
try {
entityManager.persist(product);
entityManager.flush();
}catch (RuntimeException ex) {
System.err.println(ex.getCause().getMessage());
}
return product;
}
Here if we try to persist and new entity with an id already existing in the database it will throw will throw the constraint violation exception as we originally wanted.
Note that there are 3 scenarios here:
1. Setting ID manually
If there is no choice(like the OP), i.e if you are setting your own id "manually", Spring Data JPA is assuming that you want to check if there are duplicates(hence the SELECT), so it will do a "(i)SELECT + (ii)INSERT" if there is no existing record or a "(i)SELECT + (ii)UPDATE" if there is already an existing record.
In short, 2 SQLs!
2. Use an ID Generator
Cleaner & better, for example:
#Id
#GeneratedValue(generator = "my-uuid")
#GenericGenerator(name = "my-uuid", strategy = "uuid2")
private UUID id;
Result: there is ALWAYS only 1 INSERT statement.
3. Implement Persistable and isNew()
This has already been brilliantly answered by #adarshr, but is also more painful, i.e to implement Persistable(instead of Serializable), and implement the isNew() method.
Result: Also, 1 INSERT statement.
According to Spring Data documentation Spring persists an entity if does not exists or merge, this means update, the existing one:
Saving an entity can be performed via the CrudRepository.save(…)-Method. It will persist or merge the given entity using the underlying JPA EntityManager. If the entity has not been persisted yet Spring Data JPA will save the entity via a call to the entityManager.persist(…)-Method, otherwise the entityManager.merge(…)-Method will be called.

What caused the PersistenceException with the message "detached entity passed to perist"

I'm using:
Quarkus with JPA (javax)
Postgres 11 database
I have:
An Entity
#Entity
#Table(name = "MyEntityTable")
#NamedQuery(name = MyEntity.DOES_EXIST, query = "SELECT x FROM MyEntity x WHERE x.type = :type")
public class MyEntity {
public static final String DOES_EXIST = "MyEntity.DoesExists";
#Id
#SequenceGenerator(name = "myEntitySequence", allocationSize = 1)
#GeneratedValue(generator = myEntitySequence)
private long id;
#Column(name = type)
private String type;
}
A repository
#ApplicationScoped
#Transactional(Transactional.TxType.Supports)
public class MyEntityReporitory {
#Inject
EntityManager entityManager;
#Transactional(Transactional.TxType.Required)
public void persist(final MyEntity entity) {
entityManager.persist(entiy);
}
public boolean doesExist(final String type) {
final TypedQuery<MyEntity> query = entityManager
.createNamedQuery(MyEntity.DOES_EXIST, MyEntity.class)
.setParameter("type", type);
return query.getResultList().size() > 0;
}
}
A test with two variations
Variation 1
#QuarkusTest
#QuarkusTestResource(DatabaseResource.class) // used to set up a docker container with postgres db
public class MyEntityRepositoryTest {
private static final MyEntity ENTITY = entity();
#Inject
MyEntityRepository subject;
#Test
public void testDoesExist() {
subject.persist(ENTITY);
final boolean actual = subject.doesExist("type");
assertTrue(actual);
}
#Test
public void testDoesExist_notMatching() {
subject.persist(ENTITY);
final boolean actual = subject.doesExist("another_type");
assertFalse(actual);
}
private static MyEntity entity() {
final MyEntity result = new MyEntity();
result.setType("type")
return result;
}
}
When I execute this test class (both tests) I'm getting the following Exception on the second time the persist method is called:
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist com.mypackage.MyEntity
...
Variation 2
I removed the constant ENTITY from the test class, instead I'm calling now the entity() method inside the tests, like:
...
subject.persist(entity());
...
at both places. Now the Exeption is gone and everything is fine.
Question
Can someone explain to me, why this is the case (why variante 2 is working and variante 1 not)?
https://vladmihalcea.com/jpa-persist-and-merge/
The persist operation must be used only for new entities. From JPA perspective, an entity is new when it has never been associated with a database row, meaning that there is no table record in the database to match the entity in question.
testDoesExist executed, ENTITY saved to database and ENTITY.id set to 1
testDoesExist_notMatching executed and persist called on ENTITY shows the error beacuse it exists in the database, it has an id assigned
The simplest fix is to call entity() twice, as in you variation 2.
But don't forget that the records will exist after a test is run, and might affect your other test cases. You might want to consider cleaning up the data in an #After method or if you intend to use this entity in multiple test cases then put the perist code into a #BeforeClass method.

How to get Column names of JPA entity

All my JPA entity classes implement an interface called Entity which is defined like this:
public interface Entity extends Serializable {
// some methods
}
Some of the fields of my JPA entity have #Column annotation on top of them and some don't. MyEntity class is defined like below:
#Entity
public class MyEntity implements Entity {
#Id
private Long id; // Assume that it is auto-generated using a sequence.
#Column(name="field1")
private String field1;
private SecureString field2; //SecureString is a custom class
//getters and setters
}
My delete method accepts an Entity.
#Override
public void delete(Entity baseEntity) {
em.remove(baseEntity); //em is entityManager
}
Whenever the delete method is invoked I want three things inside my delete method:
1) Fields of MyEntity that are of type SecureString
2) Column name of that particular field in DB (The field may or may not have #Column annotation)
3) The value of id field
Note that when the delete() method is invoked, we don't know for which entity it is invoked, it may be for MyEntity1, MyEntity2 etc.
I have tried doing something like below:
for (Field field : baseEntity.getClass().getFields()) {
if (SecureString.class.isAssignableFrom(field.getType())) {
// But the field doesn't have annotation #Column specified
Column column = field.getAnnotation(Column.class);
String columnName = column.name();
}
}
But this will only work if the field has #Column annotation. Also it doesn't get me other two things that I need. Any ideas?
Hibernate can use different naming strategies to map property names, which are defined implicitly (without #Column(name = "...")). To have a 'physical' names you need to dive into Hibernate internals. First, you have to wire an EntityManagerFactory to your service.
#Autowired
private EntityManagerFactory entityManagerFactory;
Second, you have to retrieve an AbstractEntityPersister for your class
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
AbstractEntityPersister persister = ((AbstractEntityPersister)sessionFactory.getClassMetadata(baseEntity.getClass()));
Third, you're almost there with your code. You just have to handle both cases - with and without #Column annotation. Try this:
for (Field field : baseEntity.getClass().getFields()) {
if (SecureString.class.isAssignableFrom(field.getType())) {
String columnName;
if (field.isAnnotationPresent(Column.class)) {
columnName = field.getAnnotation(Column.class).name();
} else {
String[] columnNames = persister.getPropertyColumnNames(field.getName());
if (columnNames.length > 0) {
columnName = columnNames[0];
}
}
}
}
Note that getPropertyColumnNames() retrieves only 'property' fields, that are not a part of primary key. To retrieve key column names, use getKeyColumnNames().
And about id field. Do you really need to have all #Id's in child classes? Maybe would better to move #Id to Entity class and mark this class with #MappedSuperclass annotation? Then you can retrieve it just with baseEntity.getId();

JPA/Hibernate Static Metamodel Attributes not Populated -- NullPointerException

I would like to use JPA2 Criteria API with metamodel objects, which seems to be pretty easy:
...
Root<JPAAlbum> albm = cq.from(JPAAlbum.class);
... albm.get(JPAAlbum_.theme) ... ;
but this Root.get always throws a NullPointerException. JPAAlbum_.theme was automatically generated by Hibernate and looks like
public static volatile SingularAttribute<JPAAlbum, JPATheme> theme;
but it's obviously never populated.
Am I missing a step in the initialization of the framework ?
EDIT: here is a snippet of how I use JPA and the metamodel when it's crashing:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<JPAAlbum> cq = cb.createQuery(JPAAlbum.class) ;
Root<JPAAlbum> albm = cq.from(JPAAlbum.class);
cq.where(cb.equal(albm.get(JPAAlbum_.theme).get(JPATheme_.id),
session.getTheme().getId())) ;
(JPAAlbum_ is a class, so I just import before) and the associated stacktrace:
Caused by: java.lang.NullPointerException
at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:138)
at net.wazari.dao.jpa.WebAlbumsDAOBean.getRestrictionToAlbumsAllowed(WebAlbumsDAOBean.java:55)
EDIT 2:
In the JBoss EntityManager guide, I can see that
When the Hibernate EntityManagerFactory is being built, it will look for a canonical metamodel class for each of the managed typed is knows about and if it finds any it will inject the appropriate metamodel information into them, as outlined in [JPA 2 Specification, section 6.2.2, pg 200]
I could also verify with
for (ManagedType o : em.getMetamodel().getManagedTypes()) {
log.warn("___") ;
for (Object p : o.getAttributes()) {
log.warn(((Attribute)p).getName()) ;
}
}
that Hibernate is aware of my metamodel, the attribute names are written, however
log.warn("_+_"+JPAPhoto_.id+"_+_") ;
remains desperately empty ...
EDIT3: here is the JPAAlbum entity and its metamodel class.
What else can I tell about my configuration ...
I use Hibernat 3.5.6-Final (according to META-INF/MANIFEST.MF),
deploy on Glassfish 3.0.1
from Netbeans 6.9.1;
and the application relies on EJB 3.1,
I hope it will help !
EDIT 4:
unfortunately, the JUnit test leads to the same exception:
java.lang.NullPointerException
at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:138)
at net.wazari.dao.test.TestMetaModel.foo(TestMetaModel.java:55)
A much simpler project is available here/tarball. It only contains my entities and their metamodel, plus a JUnit test (foo crashes with metamodel, bar is okay with the usual Query.
EDIT 5:
You should be able to reproduce the problem by downloading the tarball, building the project:
ant compile
or
ant dist
and start the JUnit test net.wazari.dao.test.TestMetaModel
CLASSPATH=`sh runTest.sh` java org.junit.runner.JUnitCore net.wazari.dao.test.TestMetaModel
(edit runTest.sh to point CLASSPATH to the right location of your JUnit4-5 jar)
All the hibernate dependencies I use should be included in the archive.
I had the same issue and it was fixed by putting the Model and Model_ class into the same package.
I had a Java EE 6 application using EclipseLink on GlassFish with some #StaticMetamodel classes created and everything was working fine. When I switched to Hibernate 4 on JBoss 7, I started getting these NPEs too. I started investigating and I found this page:
http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/metamodel.html
It quotes the JPA 2 specification, section 6.2.1.1 which defines how the static metamodel classes should be built. For example, I found out by reading the spec that "the option of different packages will be provided in a future release of this specification". I had the metamodel classes in different packages and it worked fine on EclipseLink, but it's an extra feature, as the current standard indicates the following:
Metamodel classes should be in the same package as the entity classes they describe;
They should have the same name as the entity classes they describe, followed by an underscore (e.g. Product is the entity, Product_ is the metamodel class);
If an entity inherits from another entity or from a mapped superclass, its metamodel class should inherit from the metamodel class that describes its immediate superclass (e.g. if SpecialProduct extends Product, which extends PersistentObject, then SpecialProduct_ should extend Product_ which should extend PersistentObject_).
Once I followed all the rules in the spec (the above is just a summary, please refer to section 6.2.1.1 of the spec for the complete version), I stopped getting the exceptions.
By the way, you can download the specification here: http://jcp.org/en/jsr/detail?id=317 (click on "Download page" for the final release, choose to download the specification for evaluation, accept the agreement and download the file "SR-000317 2.0 Specification" - persistence-2_0-final-spec.pdf).
I can't reproduce the issue. I used some of your entities (simplified versions of JPAAlbum, JPATheme and JPATagTheme, without any interfaces), generated the metamodel classes and the following rudimentary test method (running inside a transaction) just passes:
#Test
public void foo() {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<JPAAlbum> query = builder.createQuery(JPAAlbum.class);
Root<JPAAlbum> album = query.from(JPAAlbum.class);
Assert.assertNotNull(album.get(JPAAlbum_.theme)); // no problem here
query.where(builder.equal(album.get(JPAAlbum_.theme).get(JPATheme_.id), 1L));
List<JPAAlbum> results = em.createQuery(query).getResultList();
}
FWIW, here is the generated SQL:
select
jpaalbum0_.ID as ID32_,
jpaalbum0_.AlbumDate as AlbumDate32_,
jpaalbum0_.Description as Descript3_32_,
jpaalbum0_.Nom as Nom32_,
jpaalbum0_.Picture as Picture32_,
jpaalbum0_.Theme as Theme32_
from
Album jpaalbum0_
where
jpaalbum0_.Theme=1
Tested with Hibernate EntityManager 3.5.6-Final, Hibernate JPAModelGen 1.1.0.Final, outside any container.
My suggestion would be to first try to reproduce (if reproducible) the problem in a JUnit test context.
PS: As a side note, I wouldn't store generated classes in the VCS.
Update: Here is a persistence.xml that you can use in a testing context:
<?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_2_0.xsd"
version="2.0">
<persistence-unit name="MyPu" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.stackoverflow.q3854687.JPAAlbum</class>
<class>com.stackoverflow.q3854687.JPATheme</class>
<class>com.stackoverflow.q3854687.JPATagTheme</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<!-- Common properties -->
<property name="javax.persistence.jdbc.driver" value="${jdbc.driver}" />
<property name="javax.persistence.jdbc.url" value="${jdbc.url}" />
<property name="javax.persistence.jdbc.user" value="${jdbc.user}" />
<property name="javax.persistence.jdbc.password" value="${jdbc.password}" />
<!-- Hibernate specific properties -->
<property name="hibernate.dialect" value="${jdbc.dialect}" />
<!--
<property name="hibernate.show_sql" value="true"/>
-->
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
</persistence>
I offer an alternative solution if putting the Model and Model_ in the same package does not work. You need to add one init() method to your class that builds the SessionFactory or EntityManager:
public class HibernateSessionFactory {
private static SessionFactory factory;
static {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getFactory() {
return factory;
}
public static void init(){} //does nothing but elimating the NULLPOINTEREXCEPTION
}
So when you run your application from main method or a unit test you need to call HibernateSessionFactory.init(); first. Then the NullPointerException magically disappears and the application works.
This strange behaviour seems to happen when you pass a SingularAttribute around via method parameter.
Credit goes to #Can ÜNSAL who figured it all out in this question: Hibernate/JPA - NullPointerException when accessing SingularAttribute parameter
2019-04-24
The usual issue for unpopulated metamodel class attributes, is when the metamodel classes are in a different package than the corresponding managed classes.
The latest, JPA 2.2 specification still requires to have your metamodel classes in the same package as your corresponding managed classes.
Reference: Page 238, §6.2.1.1 Canonical Metamodel
FYI, I encountered a case where Hibernate creates a metamodel attribute but never initializes it, leading to a NullPointerException's when trying to use it.
public class Upper {
public String getLabel() { return this.label; }
public void setLabel(String label) { this.label = label; }
}
public class Lower extends Upper {
#Override
public String getLabel() { return super.getLabel(); }
}
Hibernate generate a label attribute declaration in both classes:
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(Upper.class)
public abstract class Upper_ {
public static volatile SingularAttribute<Upper, String> label;
}
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(Lower.class)
public abstract class Lower_ {
public static volatile SingularAttribute<Lower, String> label;
}
...and it will initialize Upper_.label but leave Lower_.label equal to null.
Boom.
The class and the metaModel should be in the same package, i.e.
Folder entities:
Eje
Eje_
Element
Element_
I attached one example of the metamodel code
import javax.annotation.Generated;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
import java.util.Date;
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(Eje.class)
public abstract class Eje_ {
public static volatile SingularAttribute<Eje, Integer> id;
public static volatile SingularAttribute<Eje, String> name;
public static volatile SingularAttribute<Eje, Integer> users;
public static volatile SingularAttribute<Eje, Date> createdAt;
public static volatile SingularAttribute<Eje, Date> updatedAt;
public static volatile SetAttribute<Eje, FactorCritico> factorCriticos;
}
If nothing of above resolve this NPE issue, you also can check whether you are using List in your Entities relationships instead of Set.
I found out that using List's it is needed to declare ListAttribute instead of SetAttribute in the metamodel, otherwise, it provoke a NullPointerException and if you don't see the whole stack trace you will not notice that the metamodel was not initialized by your JPA specification.
Debbie's answer got me half of the way there.
There is another "name matching" voodoo gotcha.
Short version:
The "names" have to match between Model and the MetaModel for the "properties".
Longer version:
I was using non-easy-peezy names.
First the entities:
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.LinkedHashSet;
import java.util.Set;
#Entity
#Table(name = "DepartmentTable")
public class DepartmentJpaEntity implements Serializable {
#Id
#Column(name = "DepartmentKey", unique = true)
#GeneratedValue(strategy = GenerationType.AUTO)
private long departmentKey;
#Column(name = "DepartmentName", unique = true)
private String departmentName;
#Column(name = "CreateOffsetDateTime", columnDefinition = "TIMESTAMP WITH TIME ZONE")
private OffsetDateTime createOffsetDateTime;
#OneToMany(
mappedBy = "parentDepartmentJpaEntity",
cascade = CascadeType.PERSIST,
orphanRemoval = true,
fetch = FetchType.LAZY /* Lazy or Eager here */
)
private Set<EmployeeJpaEntity> employeeJpaEntities = new LinkedHashSet<>();
}
and
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import java.io.Serializable;
import java.time.OffsetDateTime;
#Entity
#Table(name = "EmployeeTable")
public class EmployeeJpaEntity implements Serializable {
#Id
#Column(name = "EmployeeKey", unique = true)
#GeneratedValue(strategy = GenerationType.AUTO)
private long employeeKey;
#Column(name = "Ssn")
private String ssn;
#Column(name = "LastName")
private String lastName;
#Column(name = "FirstName")
private String firstName;
#Column(name = "CreateOffsetDateTime", columnDefinition = "TIMESTAMP WITH TIME ZONE")
private OffsetDateTime createOffsetDateTime;
//region Navigation
#ManyToOne(fetch = FetchType.LAZY, targetEntity = DepartmentJpaEntity.class, cascade = CascadeType.PERSIST )
#JoinColumn(name = "DepartmentForeignKey")
private DepartmentJpaEntity parentDepartmentJpaEntity;
//endregion
}
Note, my somewhat not default names. pay attention to the OnetoMany and ManyToOne object names.
Now, my meta-models:
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
#javax.persistence.metamodel.StaticMetamodel(DepartmentJpaEntity.class)
public class DepartmentJpaEntity_ {
public static volatile SingularAttribute<DepartmentJpaEntity, Long> departmentKey;
public static volatile SetAttribute<DepartmentJpaEntity, EmployeeJpaEntity> employees; /* DOES NOT WORK :( */
}
but this (below) will work, because I'm magic voodoo'ing the same names:
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
#javax.persistence.metamodel.StaticMetamodel(DepartmentJpaEntity.class)
public class DepartmentJpaEntity_ {
public static volatile SingularAttribute<DepartmentJpaEntity, Long> departmentKey;
public static volatile SetAttribute<DepartmentJpaEntity, EmployeeJpaEntity> employeeJpaEntities;
}
and the other one:
import javax.persistence.metamodel.SingularAttribute;
#javax.persistence.metamodel.StaticMetamodel(EmployeeJpaEntity.class)
public class EmployeeJpaEntity_ {
public static volatile SingularAttribute<EmployeeJpaEntity, Long> employeeKey;
public static volatile SingularAttribute<EmployeeJpaEntity, DepartmentJpaEntity> parentDepartment; /*does NOT work...its null at run time...no voodoo name matching */
}
But this does work:
import javax.persistence.metamodel.SingularAttribute;
#javax.persistence.metamodel.StaticMetamodel(EmployeeJpaEntity.class)
public class EmployeeJpaEntity_ {
public static volatile SingularAttribute<EmployeeJpaEntity, Long> employeeKey;
public static volatile SingularAttribute<EmployeeJpaEntity, DepartmentJpaEntity> parentDepartmentJpaEntity;
}
One of the reasons I purposely use non "standard" names for things is to flush out these voodoo setup issues at the beginning......in order to try and fail early...vs a failLater (hopefully in QA, not in production) issue(s) because someone renamed a property which seems benign at the time.

Categories

Resources