Spring Data Envers Entity must not be null - hibernate-envers

Suppose we have audited entities with #OneToOne relation:
#Entity
#Audited
#Table(name = "product")
public class Product {
#Id
#GeneratedValue
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "active")
private boolean active;
#OneToOne(mappedBy="product", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private ProductPrice productPrice;
}
#Audited
#Entity
#Table(name = "product_price")
public class ProductPrice {
#Id
#GeneratedValue
#Column(name = "id")
private Long id;
#Column(name = "amount")
private Long amount;
#OneToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "product_id", nullable = false)
private Product product;
}
And RevisionService with the method to get revisions and find changes:
#Transactional
public Page<Revision<Long, Product>> getGroupRevisions(Long productId, int page) {
Page<Revision<Long, Product>> revisions = productRepository.findRevisions(productId, PageRequest.of(page, 5, RevisionSort.desc()));
Long priceId = revisions.getContent().get(0).getEntity().getProductPrice().getId();
Page<Revision<Long, ProductPrice>> priceRevisions = productPriceRepository.findRevisions(priceId, PageRequest.of(page, 5, RevisionSort.desc()));
return revisions;
}
Now, If I create new Product and ProductPrice records and then make changes into Product more then 5 times (5 RevInfo records would generated), I get exception:
java.lang.IllegalArgumentException: Entity must not be null!
at org.springframework.util.Assert.notNull(Assert.java:198)
at org.springframework.data.history.AnnotationRevisionMetadata.<init>(AnnotationRevisionMetadata.java:55)
at org.springframework.data.envers.repository.support.EnversRevisionRepositoryImpl.getRevisionMetadata(EnversRevisionRepositoryImpl.java:237)
at org.springframework.data.envers.repository.support.EnversRevisionRepositoryImpl.lambda$toRevisions$1(EnversRevisionRepositoryImpl.java:223)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1837)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at org.springframework.data.envers.repository.support.EnversRevisionRepositoryImpl.toRevisions(EnversRevisionRepositoryImpl.java:226)
at org.springframework.data.envers.repository.support.EnversRevisionRepositoryImpl.getEntitiesForRevisions(EnversRevisionRepositoryImpl.java:196)
at org.springframework.data.envers.repository.support.EnversRevisionRepositoryImpl.findRevisions(EnversRevisionRepositoryImpl.java:163)
After debugging I saw that this "null" entity was proxied by Hibernate and Spring Data envers could not resolve revision number in this point:
Number revNo = this.enversService.getRevisionInfoNumberReader().getRevisionNumber(revision);
Here is the link to github test project: https://github.com/aquariusmaster/spring-data-envers-bug
So my question is this a bug in Spring Data Envers or I miss something in the configuration?

As spring-data-envers team replied, upgrading boot version to 2.3.1.RELEASE solve the problem:
https://github.com/spring-projects/spring-data-envers/issues/34#issuecomment-651681687

Related

JPA Repository Query on additional table #ManytoMany

I want to do select like this in my jpa spring repository
SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC;
My enitity
#Entity
#Table(name = "symptomp")
public class Symptomp {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "symptomp_id")
private Long symptomp_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToMany(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinTable(name = "symptomp_sicknes",joinColumns = #JoinColumn(name = "symptomp_id"),inverseJoinColumns = #JoinColumn(name = "sicknes_id"))
private Set<Sicknes> sicknes = new HashSet<>();
#Entity
#Table(name = "sicknes")
public class Sicknes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sicknes_id")
private Long sicknes_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToOne(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinColumn(name = "speciesId")
private Species species;
My Symptomp repository:
public interface SymptompRepository extends JpaRepository<Symptomp, Long> {
#Query("select p from Symptomp p where name like ?1%")
public List<Symptomp> findAllBySymptompName(String symptomp);
public Symptomp findByName(String symptomp);
public List<Symptomp> findByNameIn(List<String> list);
Integer countDistinctSymptompByName(String id);
}
How I can create this select in my JPA repository?
I try get value like in select but i got error mapping bean.
You can get query result as List<Object[]> using nativeQuery=true parameter
#Query("SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC", nativeQuery=true)
List<Object[]> getQueryResult();
Other option is to create dto class with appropriate constructor
public class QueryResultDto {
Long sicknesId;
Long count;
public QueryResultDto(Long sicknesId, Long count) {
this.sicknesId = sicknesId;
this.count = count;
}
}
Then using JPQL
#Query("select new yourproject.dtopath.QueryResultDto(...")
List<QueryResultDto> getQueryResult(#Param("symptompIds") List<Long> symptompIds);
If you want to avoid a native Query the best way is to create an Entity for that JoinTable. Then you can query it easily. Additional benefit if this is that if in future a requirement will pop up that you have to store additional attributes in that relation you will have the Entity already there to do that easily.

In attribute 'employees', the "mapped by" value 'project_id' cannot be resolved to an attribute on the target entity

Hi there I am new to JPA and got following error as
In attribute 'employees', the "mapped by" value 'project_id' cannot be resolved to an attribute on the target entity.
with following code
#Entity
#Table(name = "project")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "project_id")
protected int id;
#Column(name = "project_name")
protected String name;
#ManyToMany(mappedBy = "project_id")
protected List<Employee> employees;
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
protected int id;
#Column(name = "employee_name")
protected String name;
#ManyToMany(targetEntity = Project.class)
#JoinTable(name = "project_employee", joinColumns = #JoinColumn(name = "project_id"), inverseJoinColumns = #JoinColumn(name = "employee_id"))
protected List<Project> projects;
Here If I am trying to map the inverse entity it says that attribute not found.
Please help me to solve this
As the message says your mappedBy value is wrong. It should be the name of the FIELD/PROPERTY on the other side of the BIDIRECTIONAL relation, that links back to this class. e.g Project.employees claims to link back via field Employee.project_id, yet that doesn't exist. Instead mappedBy should be projects. This would be found in any basic JPA docs

Exception while persisting JPA object in DB having one to many relation

hi have two tables in picture table a and table b as follows :
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#SequenceGenerator(name = "JOURNAL_CATEGORY_ID_GENERATOR", allocationSize = 1, sequenceName = "clm_jounal_category_config_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "JOURNAL_CATEGORY_ID_GENERATOR")
#Column(name = "CLAIM_ID")
private String claimId;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "country")
private List<ClaimDTLS> claimDetails;
}
B Primary Key:
#Embeddable
public class BPK implements Serializable {
#Column(name = "code")
private String code;
#Column(name = "CLAIM_ID")
private String claimId;
}
B Entity:
#Entity
#Table(name = "B")
public class B implements Serializable {
#EmbeddedId
protected BPK bpk;
#Column(name = "name")
private String name;
#MapsId("country_code")
#JoinColumn(name = "claimId", referencedColumnName = "claimId", insertable = false, updatable = false)
#ManyToOne
private A a;
}
when i try to persist object of A type in Db the value of table b claim id is not set and is intialized with zero.
Also primary key of table A is generated with a oracle sequence.
any help will be welcomed.
thanks in advance
Sequence values are numbers and when JPA use them as a generator it call the setter method of the entity PK. Now, you defined your PK as a string while you use a sequence and so no matching setter can be found. Change the type of you PK to be Long and things shall work

location_id cannot be null while trying save using a Spring JPA 2 repository

I've a Spring JPA 2 entity class as
#Entity
#Table(name = "hotels")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Hotel {
#Id
#Column(name = "hotelId")
#JsonProperty("hotel_id")
private Integer hotelId;
#OneToOne(targetEntity = Location.class, cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "location_id")
#JsonProperty("location")
private Location location;
....
}
and the Location class is given as
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer location_id;
#Column
#JsonProperty("latitude")
private Double latitude;
#Column
#JsonProperty("longitude")
private Double longitude;
}
When I'm doing an hotelRepository.save(hotel) I'm getting Column 'location_id' cannot be null. I get the populated Hotel by consuming a REST web-service.
Can anyone please help me! Appreciated!

QueryDSL / JPQL : how to build a join query?

I've tried to read through the QueryDSL docs but I am still very confused. I'm accustomed to writing a lot of SQL, but this is my first real crack at using QueryDSL w/ JPQL (JPA2).
I have the following entity:
#Entity
public class Provider implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
private String name;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "provider_contact", joinColumns = #JoinColumn(name = "contact_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "provider_id", referencedColumnName = "id"))
#OrderColumn
private Collection<Contact> contact;
}
where Contact is a simple entity with an id for a pk.
#Entity
public class Contact {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
/**
* User first name
*/
#NotNull
private String firstName;
/**
* User last name
*/
#NotNull
private String lastName;
}
I'm trying to write a query which returns a Contact object given a specific Contact.id and Provider.id. If the Contact object is not a part of the Provider's Contact collection, I'm looking for a null value.
I've tried the following:
public Contact getContact( long providerId, long contactId ){
Predicate p = QProvider.provider.id.eq(providerId).and(QContact.contact.id.eq(contactId));
JPQLQuery query = new JPAQuery(em);
return query.from(QProvider.provider).innerJoin(QProvider.provider.contact).where(p).singleResult(QContact.contact);
}
but I'm getting the following error:
Caused by: java.lang.IllegalArgumentException: Undeclared path 'contact'. Add this path as a source to the query to be able to reference it.
at com.mysema.query.types.ValidatingVisitor.visit(ValidatingVisitor.java:78)
at com.mysema.query.types.ValidatingVisitor.visit(ValidatingVisitor.java:30)
at com.mysema.query.types.PathImpl.accept(PathImpl.java:94)
I'm presuming it has something to do with the fact that my predicate references QContact.contact direction and not part of the QProvider.provider.contact object, but I'm really at a loss as to figure out how this should be done.
Am I even on the right track? I'm not even sure my join is correct either.
This should work
public Contact getContact(long providerId, long contactId) {
QProvider provider = QProvider.provider;
QContact contact = QContact.contact;
return new JPAQuery(em).from(provider)
.innerJoin(provider.contact, contact)
.where(provider.id.eq(providerId), contact.id.eq(contactId))
.singleResult(contact);
}