OneToOne in Hibernate causes StackOverflow Exception when calling Mongo save - spring

I have two entities :
Invoice :
#Entity
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Table(name = "invoices")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Invoice implements Serializable {
private static final long serialVersionUID = 1L;
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Column(columnDefinition = "CHAR(36)")
#Id
private String id;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "invoice")
private InvoiceSequence invoiceSequence;
... // skipped for brevity
InvoiceSequence
#Entity
public class InvoiceSequence {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long seqId;
#OneToOne
#JoinColumn(name = "invoice_id", nullable = false)
#JsonIgnore
private Invoice invoice;
... // skipped for brevity
When calling mongo save,as in :
#Override
public Invoice save(Invoice invoice) {
Invoice savedInv = invoiceRepository.save(invoice);
InvoiceSequence seq = new InvoiceSequence();
seq.setInvoice(savedInv);
InvoiceSequence savedSeq = invoiceSequenceRepository.save(seq);
savedInv.setInvoiceSequence(savedSeq);
return savedInv;
}
i get :
java.lang.StackOverflowError
at java.lang.Class.isInstance(Native Method)
at java.lang.Class.cast(Class.java:3368)
at java.lang.invoke.DirectMethodHandle$Accessor.checkCast(DirectMethodHandle.java:418)
at java.lang.invoke.DirectMethodHandle.checkCast(DirectMethodHandle.java:487)
at com.vulog.billing.domain.Invoice_Accessor_5oixbb.getProperty(Unknown Source)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:432)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:425)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:330)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:425)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:527)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:437)
What am i missing?
Thanks for any help

Related

(Do not display relationship values)

I have two entity with name of the article and article Category.
and they have one-to-many relationships.
I use #JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,property = "id")
but I cant see data of article category(category_id) in spring data rest.
ArticleCategory.class
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory", fetch = FetchType.LAZY)
private Set<Article> articles = new HashSet<>();
}
Article.class
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable {
public Article() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
#Column(name = "title")
private String title;
#Column(name = "image_url")
private String image_url;
#Column(name = "short_description")
private String short_description;
#Column(name = "text")
private String text;
#Column(name = "keywords", nullable = true)
private String keywords;
#Column(name = "visit", nullable = false)
private int visit;
#Column(name = "code", nullable = false)
private UUID code;
#Column(name = "date_created")
#CreationTimestamp
private Date dateCreated;
#Column(name = "date_updated", nullable = false)
#UpdateTimestamp
private Date dateUpdated;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
public Article(String title, String image_url, String short_description, String text, String keywords, int visit, UUID code) {
this.title = title;
this.image_url = image_url;
this.short_description = short_description;
this.text = text;
this.keywords = keywords;
this.visit = visit;
this.code = code;
}
}
Article Repository
#CrossOrigin("http://localhost:4200")
#RepositoryRestResource(collectionResourceRel = "article", path = "article")
public interface ArticleRepository extends JpaRepository<Article,Long> {
Article findByCode(UUID uuid);
}
And this is output of spring data rest
enter image description here
That is exactly because you used #JsonManagedReference and #JsonBackReference. Keep in mind the following when using them:
#JsonManagedReference is the forward part of the relationship and is the one that gets serialized normally.
#JsonBackReference is the back part of the relationship and it will be omitted from serialization.
The serialized Article object does not contain a reference to the ArticleCategory object.
If you want to have any ArticleCategory data when serializing Article you can either use #JsonIdentityInfo so that one of the properties is serialized (in this case I've chosen id for both):
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
}
#Entity
#Table(name = "article_category")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If you are only interested in categoryId another possibility would be to use #JsonIgnore on private Set<Article> articles property so that it is not serialized:
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If none of those suits your needs you might need to implement your own custom serializer. You can read more about all those options at https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.
I solved the problem using the controller
And that's why #JsonManageRefrence and #JsonBackRefrence do not work
I replaced the lazy load with the eager load in both entity
#ManyToOne(fetch = FetchType.Eager)
#JoinColumn(name = "user_id")
#JsonManageRefrence
private User user;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory",
fetch = FetchType.Eager)
#JsonBackRefrence
private Set<Article> articles = new HashSet<>();
and then add a controller
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
#RequestMapping("/getAllArticle")
public class MyController {
private ArticleRepository articleRepository;
// you must do constructor injection
#GetMapping("/getAllArticle")
public List<Article> allArticle()
{
return articleRepository.findAll();
}
}

EntityNotFoundException when using #Cacheable

On my application I have multiple entities like:
#Entity
#Data
#Builder
#ToString(of = {"id", "code", "nameContentType", "observations"})
#EqualsAndHashCode(exclude = "room")
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "desk")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Desk implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "code")
private String code;
#Lob
#Column(name = "name")
private byte[] name;
#Column(name = "name_content_type")
private String nameContentType;
#Column(name = "observations")
private String observations;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(unique = true)
private Coordinates coordinates;
#OneToMany(mappedBy = "desk")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Reservation> reservations = new HashSet<>();
#ManyToOne
#JoinColumn(name = "room_id", insertable = false, updatable = false)
#JsonIgnoreProperties(value = "desks", allowSetters = true)
private Room room;
}
All relationships represented with a collection are cached with #Cache(usage = CacheConcurrencyStrategy.READ_WRITE).
When I delete some records of related Entities I get:
javax.persistence.EntityNotFoundException: Unable to find com.**.domain.Reservation with id ***
I don't know if I have to make any extra adjustments to my cache settings or how to debug the problem
Are you sure you updated Hibernate to the latest version 5.4.32.Final? If so, and you still have the problem, please create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that reproduces the issue.

Fetching all entities with many to many relationships in repository layer : Hibernate with JPA

in my current project, I am using Hibernate with JPA.
I have entities as follows:
Entity A:
#Entity
#Table(name = "a")
public class A implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
#Column(name = "name")
private String name;
#OneToMany
private Set<Association> associations = new HashSet<>();
}
Entity B:
#Entity
#Table(name = "b")
public class B implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
#Column(name = "description")
private String description;
#OneToMany
private Set<Association> associations = new HashSet<>();
}
Entity Association:
#Entity
#Table(name = "association")
public class Association implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne
#JoinColumn(name = "a_id", referencedColumnName = "id")
A a;
#ManyToOne
#JoinColumn(name = "b_id", referencedColumnName = "id")
B b;
...other columns..
}
Now I would like to implement repository layer for A as follows:
#Repository
public interface ARepository extends JpaRepository<A, Long> {
//Here I would like to write the function/query get all the As with many-to-many relationships.
}

API Rest - OneToMany / ManyTone with JPA and Hibernate

I have the following relationship with this two entities
· Sport has Many SportTranslation
· SportTranslation belongsTo Sport
· Is Bidirectional
When I try to access from SportTranslation to Sport I recieve the following error.
java.lang.StackOverflowError: null
at java.util.AbstractCollection.toString(AbstractCollection.java:454) ~[na:1.8.0_191]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
The error is not clear and the tables are not empty.
· I have the classical following Spring MVC architecture
The controller
#RestController
#RequestMapping("/api/public/sports")
public class SportController implements ISportsController {
Logger logger = LoggerFactory.getLogger(SportController.class);
#Autowired
private SportsMethods sportMethods;
#GetMapping(value = "/")
#Override
public ResponseEntity<List<SportDTO>> getSports(Pageable pageable) {
logger.info("--- Retrieve name of class --- : " +this.getClass().getSimpleName() + " --- Method name --- : " + new Object(){}.getClass().getEnclosingMethod().getName());
List<SportDTO> sportsMethodsList = sportMethods.retreiveListSports(pageable);
logger.info("--- Final list ---" + sportsMethodsList);
return new ResponseEntity<List<SportDTO>>(sportsMethodsList,
(sportsMethodsList == null || sportsMethodsList.isEmpty()) ? HttpStatus.NO_CONTENT : HttpStatus.OK);
}
}
· Class Methods
#Component
public class SportsMethods {
#Autowired
private SportService sportService;
public List<SportDTO> retreiveListSports(Pageable pageable) {
List<SportDTO> sportList = sportService.retreiveListSports();
return sportList;
}
}
· The service
#Component
public class SportService implements ISportService {
Logger logger = LoggerFactory.getLogger(SportService.class);
#Autowired
private SportRepository sportRepository;
#Autowired
private SportAdapter sportAdapter;
#Override
public List<SportDTO> retreiveListSports() {
List<Sport> sportList = sportRepository.findAll();
logger.info("--- Retrieve List Of Sports---"+sportList);
List<SportDTO > sportListDTO = sportAdapter.convertListSport2ListSportDTO(sportList);
logger.info("--- Retrieve a List Of SportsDTO" +sportListDTO);
return sportListDTO;
}
}
· The two adapters (SportAdatper , SportTranslationAdapter)
· SportAdapter
#Component
public class SportAdapter {
Logger logger = LoggerFactory.getLogger(SportAdapter.class);
#Autowired
private SportTranslationAdapter sportTranslationAdapter;
public List<SportDTO> convertListSport2ListSportDTO(List<Sport> sportList) {
logger.info("--- Sport List adapter" + sportList);
List<SportDTO> sportDTOList = new ArrayList<SportDTO>();
for (Sport sport : sportList) {
SportDTO sportDTO = new SportDTO();
sportDTO.setId(sport.getId());
sportDTO.setClave(sport.getClave());
sportDTO.setSportTranslationDTO(sportTranslationAdapter.convertSportTranslation2SportTranslationDTO(sport.getSportTranslation()));
sportDTOList.add(sportDTO);
}
logger.info("--- Sport DTO List adapter" + sportDTOList);
return sportDTOList;
}
· SportTranslationAdapter
#Component
public class SportTranslationAdapter {
Logger logger = LoggerFactory.getLogger(SportTranslationAdapter.class);
public List<SportTranslationDTO> convertSportTranslation2SportTranslationDTO(List<SportTranslation> list) {
List<SportTranslationDTO> sportTranslationDTOList = new ArrayList<SportTranslationDTO>();
for (SportTranslation sportTranslation : list) {
SportTranslationDTO sportTranslationDTO = new SportTranslationDTO();
sportTranslationDTO.setSportId(sportTranslation.getSport().getId());
sportTranslationDTO.setName(sportTranslation.getName());
sportTranslationDTOList.add(sportTranslationDTO);
}
return sportTranslationDTOList;
}
}
· Entities Sport and SportTranslation
· Sport
#Entity
#Table(name = "sport")
public class Sport implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sport_id")
private Long id;
#Column(name = "clave")
private String clave;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "sport_id")
private List<SportTranslation> sportTranslation;
·SportTranslation
#Entity
#Table(name = "sportstranslation")
public class SportTranslation implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sportstranslation_id")
private long idSportTranslation;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "sport_id", nullable = true)
private Sport sport;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "language_id", nullable = true)
private Language languageId;
#Column(name = "name")
private String name;
· Language is working correctly ,but this is not the problem the problem is with Sport.
Could anyone helps to me?
I would suggest your entity association to updated as below:
#Entity
#Table(name = "sport")
public class Sport implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sport_id")
private Long id;
#Column(name = "clave")
private String clave;
#OneToMany(mappedBy = "sport", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private List<SportTranslation> sportTranslation;
#Entity
#Table(name = "sportstranslation")
public class SportTranslation implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sportstranslation_id")
private long idSportTranslation;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "sport_id", nullable = true)
private Sport sport;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "language_id", nullable = true)
private Language languageId;
#Column(name = "name")
private String name;
Also, annotate your service method in which you are fetching data with #Transactional.

UUID Mapping in hibernate

I have mapped a table to my table and trying to add some values in it. but I am getting errors as below
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'create, delete, read, role_id, update, id) values
(_binary'ØN_WlAs—\niÊnÙ' at line 1
my entities are
RoleSettings.java
#Entity #Table(name = "role_settings")
#Getter #Setter #Data
public class RoleSettings implements Serializable {
private static final long serialVersionUID = 8862104773442047690L;
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#ManyToOne
#JoinColumn(name = "role_id", referencedColumnName = "id", foreignKey = #ForeignKey(name = "role_settings_iam_role_FK"))
private RoleMaster roleId;
}
RoleMaster.java
#Entity #Table(name = "role")
#Getter #Setter #Data
public class RoleMaster implements Serializable {
private static final long serialVersionUID = 1792968151371176640L;
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#Column(name = "name", nullable = false, length = 255)
private String name;
}
RoleSettingsRepository.java
public interface RoleSettingsRepository extends JpaRepository<RoleSettings, UUID>{}
RoleSettingsService.java
#Service
Class RoleSettingsService {
#Autowired
private RoleSettingsRepository roleSettingsRepository;
public BaseDTO create(RoleSettings roleSettings) {
BaseDTO response = new BaseDTO();
RoleSettings newRoleSettings = new RoleSettings();
try {
newRoleSettings.setRoleId(roleSettings.getRoleId());
newRoleSettings.setAppAccessId(roleSettings.getAppAccessId());
newRoleSettings.setCreate(roleSettings.getCreate());
newRoleSettings.setUpdate(roleSettings.getUpdate());
newRoleSettings.setRead(roleSettings.getRead());
newRoleSettings.setDelete(roleSettings.getDelete());
roleSettingsRepository.save(newRoleSettings);
response.setStatusCode(200);
} catch (Exception e) {
}
return response;
}
}
RoleSettingsController.java
#RestController
#RequestMapping("/v1/rolesettings")
public class RoleSettingsController {
#Autowired
private RoleSettingsService roleSettingsService;
#PostMapping("/post")
public BaseDTO create(#RequestBody RoleSettings roleSettings) {
BaseDTO response = roleSettingsService.create(roleSettings);
return response;
}
}
my json object
{ "roleId" :{"id": "b2e64c82-ab75-41d3-bb10-e9150f314807"} }
and my roleId is stored in database as type binary(16).
Check in your database data type of the id column. It has to be BINARY(16). And annotate your entity field as:
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#Column(columnDefinition = "BINARY(16)")
private UUID id;
Note that you nned to add a column definition in this case.

Resources