Querying revisions of nested object using spring-data-envers - spring-data

I'm trying to implement entity auditing in my Java Spring Boot project using spring-data-envers. All the entities are being created as they should, but I've come up against a brick wall when executing the query.
parentRepository.findRevisions(id).stream().map(Parent::getEntity).collect(Collectors.toList());
During this select the repository is supposed to fetch info also from the child entity, instead I get unable to find <child object> with {id}.
According to my experiments categoryId is being searched in the Category_Aud table, instead of the actual table with desired data.
Code snippets:
#Data
#Entity
#Audited
#NoArgsConstructor
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
private Status status;
#Enumerated(EnumType.STRING)
private Type requestType;
private String fullName;
#ManyToOne
#JoinColumn(name = "child_id")
private Child child;
}
#Data
#Entity
#Audited
#NoArgsConstructor
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
}
I've extended Parent with RevisionRepository
#Repository
public interface ParentRepository extends RevisionRepository<Parent, Long, Long>, JpaRepository<Parent, Long>
And annotated my SpringBootApplication entry class with:
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)
I couldn't find any explanation for this so far, how can make parentRepository get what I need?

The underlying problem here is that the reference from a versioned entity isn't really properly defined. Which variant of the reference should be returned? The one at the start of the version you use as a basis, the one at the end? The one that exists right now?
There are scenarios for which each variant makes sense.
Therefor you have to query the revisions yourself and can't simply navigate to them.

Related

Set vs List in REST One To Many

I have one to many relationship. If in class Customer I write List:
private List<Orders> order;
my GetMapping will work fine.
But I want to use best practices and I write Set instead of List:
private Set<Orders> order;
In result I have error:
Could not write JSON: Infinite recursion (StackOverflowError); nested
exception is com.fasterxml.jackson.databind.JsonMappingException:
Infinite recursion (StackOverflowError)
Why I have this error? What's wrong with Set?
My entities:
#Entity
public class Customer {
#Id
#GeneratedValue
private int id;
private String firstName;
private String lastName;
#OneToMany(cascade=ALL, mappedBy="customer", orphanRemoval=true)
private Set<Orders> order;
//private List<Orders> order;
}
#Entity
public class Orders {
#Id
#GeneratedValue
private int id;
#JsonIgnore
#ManyToOne
#JoinColumn(name="customer_id", nullable=false)
private Customer customer;
}
And GetMapping:
#GetMapping("/customer/{id}")
public ResponseEntity get(#PathVariable Long id) {
Optional<Customer> customer = customerRepository.findById(id);
return new ResponseEntity<>(new ResponseObject(customer));
}
UPD. I see question Infinite Recursion with Jackson JSON and Hibernate JPA issue. But it's other question. I talk about difference in use List and Set. I am not interesting in #JsonIgnore and I don't ask about it (and I use it in my code). I want to understand why I have an error when I use Set and don't have error with List

Repository findIn confusion

My database has an Exchanges class which contains a list of CurrencyPairs.
Is it possible to use to use a Repository method to directly obtain a CurrencyPair which matches on name within a given Exchange? I'm thinking of something like
CurrencyPairDbo findByExchangeNameAndCurrencyPairIn(...)
but I can't see quite how to tie it all together. Or do I need to write a custom query for this? And does this need to be in the ExchangeRepository or the CurrencyPairRespository?
#Entity()
#Table(name = "Exchanges")
public class ExchangeDbo {
#Id #GeneratedValue
#Getter private Long id;
#Getter private String exchangeName;
#OneToMany(mappedBy = "exchange",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.EAGER)
#BatchSize(size=100)
#Getter private List<CurrencyPairDbo> listCurrencyPair = new ArrayList<>();
...
}
#Entity()
public class CurrencyPairDbo {
#Id #GeneratedValue
#Getter private Long id;
#Column(unique=true)
private String currencyPair;
#ManyToOne(fetch=FetchType.EAGER)
#Getter private ExchangeDbo exchange;
...
}
Edit:
I'm thinking it's not Find...In that I want at all. I think that something like:
List<CurrencyPairDbo> x = exchangeRepository.findByExchangeNameLowercaseAndListCurrencyPairCurrencyPair(exchangeName.toLowerCase(), currencyPair);
might work, except that in returns an Exchange object and a:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [biz.ianw.coindatabase.database.ExchangeDbo] to type [biz.ianw.coindatabase.database.CurrencyPairDbo]
This, in the currency pair repository, seems to do the job.
I added a lower case field for matching purposes and an index for efficiency.
CurrencyPairDbo findByExchangeExchangeNameLowercaseAndCurrencyPairNameLowercase( String exchangeName, String currencyPair );

The attribute [] is not present in the managed type

I'm doing an application that has this relation ship: A personal contact has an Email.
So i'm trying to find the Emails from the personal contact and I'm doing this query using Criteria but always return IllegalArgumentException:
#Override
public Email findByEmail(PersonalContact personalContact) {
CriteriaBuilder criteriaBuilder = entityManager().getCriteriaBuilder();
CriteriaQuery<Email> criteriaQuery = criteriaBuilder.createQuery(Email.class);
Root<Email> email = criteriaQuery.from(Email.class);
criteriaQuery.where(criteriaBuilder.equal(
email.get("personalContact"), criteriaBuilder.parameter(PersonalContact.class, "personalContact")));
TypedQuery<Email> typedQuery = entityManager().createQuery(criteriaQuery);
typedQuery.setParameter("personalContact", personalContact);
return typedQuery.getSingleResult();
}
Personal contact is like a foreign key.
And here is my Email class:
#Entity
#Table(name = "Email")
public class Email implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String mainEmail;
private List<String> secondaryMail;
#JoinColumn(name = "personal")
#OneToOne(fetch = FetchType.LAZY)
private PersonalContact pContact;
and here is my Personal Contact class:
#Entity
#Table(name = "PERSONALCONTACT")
public class PersonalContact extends Contact implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "PERSONAL_ID")
private Long id;
//Other variables
#OneToOne(fetch=FetchType.LAZY, mappedBy="personal")
private Email email;
And every time I execute the query this is the return:
Exception in thread "AWT-EventQueue-0"
java.lang.IllegalArgumentException: The attribute [personalContact] is
not present in the managed type [EntityTypeImpl#1230307250:Email [
javaType: class csheets.ext.crm.contact.Email descriptor:
RelationalDescriptor(csheets.ext.crm.contact.Email -->
[DatabaseTable(Email)]), mappings: 5]].
I did some search and the others programmers said the problem was on the name of the variables... but i guess the names of the variables are correct.
So what I'm doing wrong? perhaps the relationship between that two classes?
Thank you!
If you read the exception message carefully, you'll find that it is complaining that class Email does not have a property (attribute) called personalContact, and indeed, there is no such property. Presumably you meant the pContact property?
(Mistakes such as this are why I recommend querying JPA via Querydsl: code completion would likely have prevented this mistake, and even if not, you would have gotten a clear compiler message when trying to use a non-existing property)

how to develop JPA bi-directional entities in spring toot

I am new to spring boot and jpa/hibernate, please bear my inaccurate usage of the terminologies.
I have two entities: book and address. A book is published in a certain city which is stored in "address", a "address" can publish multiple books.
The DB schema for book is: id, name, author, price, addressid
schema for address: addressid, addressCountry, addressCity
The entity for book:
#Entity
#Table(name = "test_book")
public class Book implements Serializable {
private static final long serialVersionUID = 8025948150436422040L;
#Id
long id;
#Column(name = "name")
String name;
#Column(name = "author")
String author;
#Column(name = "price")
long price;
#ManyToOne
#JoinColumn(name = "addressid")
private Address address;
...//getter and setter
The entity for address
#Entity
#Table(name = "test_address")
public class Address implements Serializable{
private static final long serialVersionUID = -3541059157210384355L;
#Id
#Column(name= "addressid")
private long addressId;
#Column(name="addresscountry")
private String addressCountry;
#Column(name="addresscity")
private String addressCity;
#OneToMany(mappedBy = "address")
private Collection<Book> books;
...//getter setter
But when I call the Restful service, I get infinite loop...
[{"id":11,"name":"Java Book","author":"Jame Gosling","price":100,"address":{"addressId":1,"addressCountry":"China","addressCity":"Shanghai","books":[{"id":11,"name":"Java Book","author":"Jame Gosling","price":100,"address":...
I did some search. And my request is:
when I search a book, I can get the information: id, name, author, price, address..
And also I can query a address to get all the books the city published.
When I add Json Annotation #JsonManagedReference in address and #JsonBackReference in book entity, I can query book but cannot get address information.
Could you please help how to solve the problems? Thank you very much.
You can ignore the #JsonIgnore on getter for books. This will exclude the Collection<Book> books property from serialization the Address.
Link : JacksonAnnotations - Faster XML Wiki
Example:
#JsonIgnore
public Collection<Book> getBooks() {
...
}
#JsonIgnore
The Jackson annotation #JsonIgnore is used to tell Jackson to ignore a
certain property (field) of a Java object. The property is ignored
both when reading JSON into Java objects, and when writing Java
objects into JSON.
In your case this is happening as there is bidirectional relationship and so it will go into loop. To stop this you need to provide JsonIgnore
And so your code will be like :
-> The entity for address
#Entity
#Table(name = "test_address")
public class Address implements Serializable{
private static final long serialVersionUID = -3541059157210384355L;
#Id
#Column(name= "addressid")
private long addressId;
#Column(name="addresscountry")
private String addressCountry;
#Column(name="addresscity")
private String addressCity;
#OneToMany(mappedBy = "address")
#JsonIgnore
private Collection<Book> books;

how to force ejb3 to reload value from data base and not use those of the context

Hello here I have a big problem that I hope to find help
here I have two entities
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="Role", discriminatorType=DiscriminatorType.STRING)
public class Utilisateur implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#Column(name="nom",nullable=false)
private String nom;
#Column(name="Role",nullable=false, insertable=false)
private String Role ;
//...
}
#Entity
#Table(name="ResCom")
#DiscriminatorValue("ResCom")
public class ResCom extends Utilisateur {
/...
}
the first thing I do
ResCom rsCom= new ResCom(nom,prenom, email,civilite,
SysQl.crypePasse(pass));
gr.create(rsCom);
I check my database I see that property is ResCom insert
but when I check the value of role I get null
Utilisateur tets= gr.findByEmail(email);
message=tets.getEmail()+" and Role :"+tets.getRole()+"";
but in my bass it ResCom !!!!!
the problem disappears when I deploy the project again
I hope you have a solution
And thank you in advance
sorry for my English
Without seeing how you persist, load your entity, i recommend using entity manager's refresh method. Something like,
entityManager.refresh(yourEntity);