#JsonIgnoreProperties doesn't work properly
I'm writing a one to many relation, one property can have multiply propertySale, I try to use #JsonIgnoreProperties to avoid infinite recursion, but for some reason it doesn't work when I try to save a propertySale. Counld someone please tell me where I do wrong?
In Property.java
#Data
#Getter
#Entity
#Table(name = "Property")
public class Property {
#Id
#GeneratedValue
private Long id;
...
#OneToMany(mappedBy="property", cascade = CascadeType.ALL, targetEntity = PropertySale.class)
#JsonIgnoreProperties("property")
private Set<PropertySale> propertySales = new HashSet<>();
...
public void addPropertySale(PropertySale propertySale){
this.propertySales.add(propertySale);
}
}
In PropertySale.java
#Data
#Getter
#Entity
#Table(name = "PropertySale")
public class PropertySale {
#Id
#GeneratedValue
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "property_id", referencedColumnName = "id")
#JsonIgnoreProperties("propertySales")
private Property property;
...
In PropertySaleServiceImp.java
#Service
public class PropertySaleServiceImp implements PropertySaleService{
#Autowired
private PropertySaleRepository propertySaleRepository;
#Autowired
private PropertyRepository propertyRepository;
#Override
public ResponseEntity<PropertySale> savePropertySale(PropertySale propertySale) {
Optional<Property> existPropertyOpt = this.propertyRepository.findById(propertySale.getProperty().getId());
if(existPropertyOpt.isPresent()){
Example<PropertySale> propertySaleExample = Example.of(propertySale);
Optional<PropertySale> existPropSale = this.propertySaleRepository.findOne(propertySaleExample);
if(existPropSale.isPresent()){
throw new PropertySaleAlreadyExistException();
}else{
Property existProperty = existPropertyOpt.get();
propertySale.setProperty(existProperty);
existProperty.addPropertySale(propertySale);
this.propertyRepository.save(existProperty);
return new ResponseEntity<>(propertySale, HttpStatus.CREATED);
}
}else{
throw new PropertyNotFoundException(propertySale.getProperty().getId());
}
}
I get
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError
when the line this.propertyRepository.save(existProperty);
is executed.
Related
I have a 3 models and 1 table to Many to many relationship on my project
this:
#Embeddable
#Getter
#Setter
public class ProductWarehouseId implements Serializable {
#Column(name = "warehouse_Id")
private Long warehouseId;
#Column(name = "product_Id")
private Long productId;
#Column(name = "user_Id")
private Long userId;
public ProductWarehouseId() {
}
public ProductWarehouseId(Long warehouseId, Long productId, Long userId) {
this.warehouseId = warehouseId;
this.productId = productId;
this.userId = userId;
}
}
---------------------------------------------------
#Entity
#NoArgsConstructor
#Getter
#Setter
public class ProductWarehouse {
#EmbeddedId
ProductWarehouseId productWarehouseId;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("productId")
#JoinColumn(name = "product_id")
ProductEntity product ;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("warehouseId")
#JoinColumn(name = "warehouse_id")
WarehouseEntity warehouse ;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("userId")
#JoinColumn(name = "user_id")
UserEntity userEntity;
#Column(name = "stockAmount")
private Long stockAmount;
#Column(name = "transctionDate")
#Temporal(TemporalType.TIMESTAMP)
private Date transactionDate = new Date();
public ProductWarehouse(ProductEntity product, UserEntity user) {
this.product = product;
this.userEntity = user;
}
}
********************************************************
#Getter
#Setter
#Entity
#RequiredArgsConstructor
public class ProductEntity extends BaseEntity{
#OneToMany(mappedBy = "product",cascade = CascadeType.ALL)
private Set<ProductWarehouse> productWarehouses;
//And more veriables
}
------------------------------------
#Getter
#Setter
#Entity
public class WarehouseEntity extends BaseEntity{
#OneToMany(mappedBy = "warehouse",cascade = CascadeType.ALL)
private Set<ProductWarehouse> productWarehouses = new HashSet<>();
//and more veriables
}
When i trying to select list from product_warehouse table to make changes, i have some Exceptions.
I want to transfer the products between warehouses using fromId and toId
I using this method in service class:
#Override
#Transactional
public void transfer(Long fromId, Long toId) {
WarehouseEntity warehouseEntity = warehouseCRUDRepository.getOne(fromId);
WarehouseEntity warehouseEntity1 = warehouseCRUDRepository.getOne(toId);
if (warehouseEntity.getStatus().equals(WarehouseStatus.ACTIVE) && warehouseEntity1.getStatus().equals(WarehouseStatus.ACTIVE)){
Collection<ProductWarehouse> productWarehouses = em
.createNativeQuery("select c from product_warehouse c where c.warehouse_id =:fromId")
.setParameter("fromId",fromId)
.getResultList();
for (ProductWarehouse p : productWarehouses){
p.getProductWarehouseId().setWarehouseId(toId);
p.setWarehouse(warehouseCRUDRepository.getOne(toId));
}
}
}
And the Exception is :
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2002] with root cause.
Can you hep me please.
I am sorry for my English, and thank you.
ProductWarehouse is already having Warehouse in it, I don't understand why are you again setting it up by fetching it from DB inside the for loop.
I don't see any necessity of the for loop in that method, also as you described above you haven't defined many to many relationship anywhere. while building the relationship you can use the join table as explained here
if you need more information, please share more details of your need and errors you are facing.
I try to save object Run to database. I defined relation between Run and City. One city could have many runs. I got problem with city_id. Is null.
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
java.sql.SQLIntegrityConstraintViolationException: Column 'city_id' cannot be null
My entieties and controller:
City
#Entity
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "cities")
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "city_id")
private long id;
#OneToMany(mappedBy = "city", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Run> runs = new ArrayList<>();
private String name;
}
Run
#Entity
#Builder
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "runs")
public class Run {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name_run")
private String nameRun;
#Column(name = "distance")
private double distance;
#Column(name = "date")
private Date date;
#Column(name = "my_time")
private String myTime;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "city_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private City city;
}
Controller
#CrossOrigin
#RestController
#RequestMapping("/api/")
public class RunController {
private RunRepository runRepository;
private RunService runService;
public RunController(RunRepository runRepository, RunService runService) {
this.runRepository = runRepository;
this.runService = runService;
}
#GetMapping("runs")
public ResponseEntity<List<Run>> getRuns() {
return runService.getRuns();
}
#PostMapping("runs")
public ResponseEntity addRun(#RequestBody Run run) {
return new ResponseEntity<>(runRepository.save(run), HttpStatus.OK);
}
}
I would like to save the run in DB.
My test request looks like :
{
"nameRun": "test",
"distance":"5.0",
"date":"2020-12-12",
"myTime":"50:40",
"city":"test1"
}
Result from evaluate expresion in Intelijj:
Why the City = null? Is here error in mapping?
Can you try with this json but you need to pass city id in json.
{
"nameRun": "test",
"distance": "5.0",
"date": "2020-12-12",
"myTime": "50:40",
"city": {
"id": 1,
"name": "test1"
}
}
Thanks
First of all, use Long for id please. It is better to add #Entity annotation too.
#Entity
public class City {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "city", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Run> runs = new ArrayList<>();
}
#Entity
public class Run {
#Id
#GeneratedValue
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
private City city;
}
You need to set city_id when you save Run.
The simplest way to do that is just create a fake transient City and set id to it.
City city = new City();
city.setId(1L);
Run run = new Run();
run.setCity(city);
repository.save(run);
Obviously you should have a city with id 1L in the database.
Other options are
Use something like session.load() Hibernate analogue with Spring repository to create City without loading it from datatbase.
Load City entity entirely by id.
if you wanna save any run class,
Run run = new Run();
City city = new City();
city.getRuns().add(run);
runRepository.save(run);
if you wanna save any run class, first you need to insert to (Arraylist) runs variable of city class like city.getRuns().add(run) after filling run then you can runRepository.save(run).
Also my samples are here. You can look at myclasses.
First class is called Patient .
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#ToString
#Table(name = "aapatient")
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "AA_PATIENT_SEQ")
#SequenceGenerator(sequenceName = "AA_PATIENT_SEQ", allocationSize = 1, name = "AA_PATIENT_SEQ")
#Column(name = "patientid")
private Long patientid;
private String name;
private String lastname;
#OneToMany(mappedBy = "patient", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Problem> problems;
}
Second Class called Problem is this one.
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Entity
#Table(name="aaproblem")
public class Problem{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "AA_PATIENT_SEQ")
#SequenceGenerator(sequenceName = "AA_PATIENT_SEQ", allocationSize = 1, name = "AA_PATIENT_SEQ")
#Column(name = "problemid")
private Long problemid;
private String problemName;
private String problemDetail;
#Temporal(TemporalType.TIMESTAMP)
Date creationDate;
#NotNull
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "patient_id")
private Patient patient;
}
When I'm trying to save an U object I got next exception:
org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [com.roc.domain.A.user]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.roc.domain.A.user]
I have two tables:
1. user that columns are id(auto incr, primary), name.
2. contact that columns are id, user_id(that is foreign key -> user.id) and address.
#Entity
#Table(name = "a")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name="address")
private String address;
#OneToOne
#MapsId
private U user;
public A() {
}
// getters and setters
}
#Entity
#Table(name = "u")
public class U {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name="username")
private String userName;
#JoinColumn(name = "user_id", referencedColumnName = "id")
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private A a;
public U(){};
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Test
public void simpleCrudTest() {
U user = new U("name", new A("address"));
uRepository.save(user);
}
}
You have set the cascade correctly however because the relationship is bi-directional you need to set both sides in the in-memory model.
#Test
public void simpleCrudTest() {
U user = new U("name", new A("address"));
//will work when this is added
a.setUser(user);
uRepository.save(user);
}
Otherwise, as the error states, A has a null reference for user on save.
Edit: To save using a single repository save call.
#Entity
#Table(name = "a")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "address")
private String address;
#OneToOne
#MapsId
private U user;
public A() {
}
}
#Entity
#Table(name = "u")
public class U {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#JoinColumn(name = "user_id", referencedColumnName = "id")
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private A a;
public U() {
};
// method to manage the bidirectional association
public U addToA(A a) {
this.a.add(a);
a.setUser(this);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Test
public void simpleCrudTest() {
U user = new U();
user.addToA(new A("address"));
user.setUserName("username");
uRepository.save(user);
}
}
Also, you refer to this link.
inserting values into multiple tables using hibernate
You have to save A first, Then set saved A to U and save U.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Autowired
private ARepository aRepository;
#Test
#Trascational
public void simpleCrudTest() {
A a = new A();
a.setAddress("address");
a = aRepository.save(a);
U user = new U("name", a);
uRepository.save(user);
}
}
So I am getting the exception:
org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: se.mulander.cosmos.movies.model.Cast.starredIn in se.mulander.cosmos.movies.model.ExtendedMovie.cast
But I can't really figure out why.
The two objects that I am going to map are:
#Entity
#Table(name = "cast")
#ApiModel(description = "A cast member that has been part of making the movie")
public class Cast
{
#JsonIgnore
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JoinColumn(name = "movie_id")
public ExtendedMovie starredIn;
}
and
#Entity
#Table(name = "extended_movie")
public class ExtendedMovie
{
#OneToMany(cascade = {CascadeType.ALL}, mappedBy = "starredIn", orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
public List<Cast> cast = new ArrayList<>();
}
I have stripped them of some other properties, but in essence this is the relationship that is not working.
So what I don't get is why it says that it is an unknown property, as the property is public and hibernate shouldn't have any problems mapping it.
what is it that I am missing here?
Try something like:
ExtendedMovie :
#Entity
public class ExtendedMovie implements Serializable {
private static final long serialVersionUID = 6771189878622264738L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "cast_id", referencedColumnName = "id")
private Set<Cast> cast;
public Set<Cast> getCast() {
return cast;
}
public void setCast(Set<Cast> cast) {
this.cast= cast;
}
}
Cast:
#Entity
#ApiModel(description = "A cast member that has been part of making the movie")
public class Cast implements Serializable {
private static final long serialVersionUID = 6771189878622265738L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
//Remove extendedmovie from here
//other property getter and setters here
}
This will establish a one-to-many relationship between ExtendedMovie and Cast.
I have a model like this one below:
#Entity(name = "request")
public class VisitRequest {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "visitRequest", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private List<Visitor> visitors;
//default constructor, getters and setters
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class")
public class Visitor {
#Id
#GeneratedValue
private Long id;
#ManyToOne
#JsonBackReference
private VisitRequest visitRequest;
//default constructor, getters and setters
}
#Entity
public class ContactPerson extends Visitor {
private PhoneNumber phoneNumber;
//default constructor, getters and setters
}
But when I try to update a visitRequest by exchanging one of the visitors with a contact person, and try to execute the method on a CRUD repository visitRequestRepository.save(visitRequest); I'm getting this exception:
Servlet.service() for servlet [dispatcherServlet] in context with path
[] threw exception [Request processing failed; nested exception is
org.springframework.orm.ObjectRetrievalFailureException: Object
[id=null] was not of the specified subclass
[cern.ais.visits.core.domain.visitor.Visitor] : class of the given
object did not match class of persistent copy; nested exception is
org.hibernate.WrongClassException: Object [id=null] was not of the
specified subclass [cern.ais.visits.core.domain.visitor.Visitor] :
class of the given object did not match class of persistent copy] with
root cause
Maybe the problem is that in the database there is the same id used in the contact_person and visitor tables?
How can I solve the problem? I've searched for the solutions but none worked for me.
You're probably not initializing visitRequest reference in ContactPerson. Please take a look at the following configuration, it works with Spring Boot JPA. I have used lombok to generate Getter and Setters.
Here's a working example implemented in Spring Boot https://github.com/ConsciousObserver/SpringBootJpaInheritance
#Data
#Entity(name = "request")
class VisitRequest {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "visitRequest", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private List<Visitor> visitors = new ArrayList<>();
}
#NoArgsConstructor
#Data
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class")
class Visitor {
#Id
#GeneratedValue
private Long id;
#ManyToOne
#JsonBackReference
private VisitRequest visitRequest;
public Visitor(VisitRequest visitRequest) {
this.visitRequest = visitRequest;
}
}
#NoArgsConstructor
#Data
#ToString(callSuper = true)
#EqualsAndHashCode(callSuper = true)
#Entity
class ContactPerson extends Visitor {
private String phoneNumber;
public ContactPerson(VisitRequest visitRequest, String phoneNumber) {
super(visitRequest);
this.phoneNumber = phoneNumber;
}
}
I suspect the reason you are having this issue is your annotations #Id and #GeneratedValue are not inherited. If you define property id in ContactPerson you will have a generated Id and will not be a problem anymore.
Try changing ContactPerson class to:
#Entity
public class ContactPerson extends Visitor {
#Id
#GeneratedValue
private Long id;
private PhoneNumber phoneNumber;
//default constructor, getters and setters
}
#Entity(name = "request")
public class VisitRequest {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "visitRequest", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private List<Visitor> visitors;
//default constructor, getters and setters
}
#Entity(name = "visitor")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "visitor_type", discriminatorType = DiscriminatorType.STRING, length = 16)
#DiscriminatorValue(value = "visitor")
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class")
public class Visitor {
#Id
#GeneratedValue
private Long id;
#ManyToOne
#JsonBackReference
private VisitRequest visitRequest;
//default constructor, getters and setters
}
#Entity(name = "contact_person")
#Table(name = "contact_person")
#DiscriminatorValue(value = "contact_person")
public class ContactPerson extends Visitor {
private PhoneNumber phoneNumber;
//default constructor, getters and setters
}
And you able to persist ContactPerson like contactPersonDao.save(contactPerson),
you can`t persist field with visitor link.
I am not expert but it work for me.
And you can persist visitor as visitor if he not contactPerson
It seems the id's between different tables has same value, so when hibernate is trying to load an entity with a specific id and if another entity with same id is already present in memory then hibernate is complaining about this issue.