Fetching part of one to many property using Spring Data Projections - java

I want to return a tuple of Parent.id field and List<Child.id>.
Parent:
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
#Entity
public class Parent implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "id")
private Long parentId;
//we actually use Set and override hashcode&equals
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> children = new ArrayList<>();
public void addChild(Child child) {
child.setParent(this);
children.add(child);
}
public void removeChild(Child child) {
child.setParent(null);
children.remove(child);
}
public Long getParentId() {
return id;
}
public List<Child> getReadOnlyChildren() {
return Collections.unmodifiableList(children);
}
}
Child:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import java.io.Serializable;
#Entity
public class Child implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "id")
private Long childId;
#ManyToOne
#JoinColumn(name = "id")
private Parent parent;
public Long getChildId() {
return id;
}
public Parent getParent() {
return parent;
}
/**
* Only for usage in {#link Parent}
*/
void setParent(final Parent parent) {
this.parent = parent;
}
}
The Spring Data Projection:
import java.util.List;
interface IdAndChildrenIds {
Long getParentId();
List<ChildId> getChildren();
}
interface ChildId {
Long getChildId();
}
The ParentRepository this is where problems begin:
import org.springframework.data.repository.CrudRepository;
public interface ParentRepository extends CrudRepository<Parent, Long> {
IdAndChildrenIds findIdAndChildrenIdsById(Long id);
}
But that doesn't work because the property doesn't comply with JavaBean standard (getter getReadOnlyChildren instead of getChildren), so I configured ObjectMapper to recognize private fields:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
#Configuration
#EnableWebMvc
public class HibernateConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
ObjectMapper mapper = new Jackson2ObjectMapperBuilder().build();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
converters.add(new MappingJackson2HttpMessageConverter(mapper));
}
}
Then, it still doesn't work because the property is LAZY initialized and it cannot be fetched outside a transaction (and because I wrote spring.jpa.open-in-view=false in application.properties due to that being a better practice). So, I must specify explicit join using query and also must use aliases so Spring Data recognizes the properties:
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface ParentRepository extends CrudRepository<Parent, Long> {
#Query("select " +
" c.parent.parentId as parentId, " +
" c.childId as childId" +
"from Child c inner join a.parent p " +
"where p.parentId=:id")
IdAndChildrenIds findIdAndChildrenIdsById(#Param("id") long id);
}
But this again doesn't work javax.persistence.NonUniqueResultException: result returns more than one elements because the specified select gives a list of tuples: List<{parentId, childId}>, while I want one tuple of {parentId, List<childId>}.
So, regarding this answer, I added #Value("#{target.parentId}") to Long getParentId();. But that did not have any effect in my case. I still get NonUniqueResultException.
Then, I tried changing the return value of the method from IdAndChildrenIds to IdAndChildrenIds just to see if the error goes away, even though that solution would not help. But that didn't work either:
Could not write JSON: No serializer found for class org.springframework.aop.framework.DefaultAdvisorChainFactory and no properties discovered to create BeanSerializer
As I said, field visibility is already set to ANY.
Versions:
- Spring Boot 1.5.9.RELEASE
- Spring Boot Starter Data JPA
- Spring Boot Starter Web
- Spring HATEOAS

Looking at this now, weird that I want parent id and ids of its children while knowing the parent id already.
interface ChildRepo{
#org.spring...Query(value = "select id from children where parent_id = :parentId", nativeQuery = true)
List<Long> findIdsByParentId(Long parentId);
}
#lombok.Value
class IdsDto{
Long parentId;
List<Long> childrenIds;
}
public IdsDto createTupleThing(Long parentId){
return new IdsDto(parentId, childRepo.findIdsByParentId(parentId);
}

Related

On Spring, how to query DynamoDB table using JPA and a composite key (Partition Key and Sort Key)?

I have a Spring project set up with JPA and Spring Data DynamoDB. It works alright. I can obtain items from the DynamoDB table by reading it by Partition Key and the Sort key (referred as DynamoDBHashKey and DynamoDBRangeKey).
My problem is that the way my repository is set up, the table is being read using query and scan operations, instead of get-item operation, which should be more efficient.
This is my entity:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#DynamoDBTable(tableName = "my-entity-table")
public class MyEntity {
#Id
#DynamoDBHashKey
#DynamoDBAttribute(attributeName = "partition_key")
private String partitionKey;
#Id
#DynamoDBRangeKey
#DynamoDBAttribute(attributeName = "sort_key")
private String sortKey;
...
}
And this is my repository:
import org.socialsignin.spring.data.dynamodb.repository.EnableScan;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
#EnableScan
#Repository
public interface MyEntityRepository extends CrudRepository<MyEntity, String> {
List<MyEntity> findByPartitionKeyAndSortKey(String partitionKey, String sortKey);
}
How do I configure my entity and repository to read items from the table using the get-item operation when my table has both a Partition Key and Sort Key?
Having done some research I stumbled onto this two articles:
Composite Primary Keys Kotlin Example
Spring Data JPA with a Hash & Range Key DynamoDB Table
The first one explains how to do what I want in Kotlin. Not bad, but it is not exactly what I'm looking for.
The second one hits the target perfectly, basically what it says is that I need to create a Primary Key object for my entity object, like this:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBDocument;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIgnore;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBRangeKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#DynamoDBTable(tableName = "my-entity-table")
public class MyEntity {
#Id
#DynamoDBIgnore
private PrimaryKey primaryKey;
...
#DynamoDBHashKey
#DynamoDBAttribute(attributeName = "partition_key")
public String getPartitionKey() {
return primaryKey != null ? primaryKey.getPartitionKey() : null;
}
public void setPartitionKey(final String partitionKey) {
if (primaryKey == null) {
primaryKey = new PrimaryKey();
}
primaryKey.setPartitionKey(partitionKey);
}
#DynamoDBRangeKey
#DynamoDBAttribute(attributeName = "sort_key")
public String getSortKey() {
return primaryKey != null ? primaryKey.getSortKey() : null;
}
public void setSortKey(final String sortKey) {
if (primaryKey == null) {
primaryKey = new PrimaryKey();
}
primaryKey.setSortKey(sortKey);
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#DynamoDBDocument
public static class PrimaryKey {
#DynamoDBHashKey
#DynamoDBAttribute(attributeName = "partition_key")
private String partitionKey;
#DynamoDBRangeKey
#DynamoDBAttribute(attributeName = "sort_key")
private String sortKey;
}
}
Then, I don't need to create any custom query methods on my repository class:
#EnableScan
#Repository
public interface MyEntityRepository extends
CrudRepository<MyEntity, MyEntity.PrimaryKey> {
}
And after that, it is just matter of using JPA's CrudRepository methods to obtain the items, like this:
final MyEntity.PrimaryKey myEntityPK
= new MyEntity.PrimaryKey("partitionKey", "sortKey");
final MyEntity myEntity = myEntityRepository.findById(myEntityPK)
.orElseThrow(() -> ... );
To verify that it actually is using the get-item operation instead of the scan and query operations, one could place a couple of breakpoints on the following classes (as of spring-data-dynamodb-5.1.0):
org.socialsignin.spring.data.dynamodb.core.DynamoDBTemplate
org.socialsignin.spring.data.dynamodb.repository.support.SimpleDynamoDBCrudRepository

How to set parentId in Child Entity of one to many mapping using Spring Boot Data JPA

Use Case: We have one - many bidirectional relationships and we will be receiving requests to update as a parent which contains either child being modified or not.
Technology stack
Spring boot 2.0.2
Spring data Jpa
Sample Code:
Parent Class Entity:
package com.example.demo.model;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter
#Setter
#NoArgsConstructor
#DynamicInsert
#DynamicUpdate
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String a;
private String b;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "parent")
private Set<Child> childs = new HashSet<>();
public void addChild(Child child) {
childs.add(child);
child.setParent(this);
}
public void removeChild(Child child) {
childs.remove(child);
child.setParent(null);
}
public void setChilds(
Set<Child> childrens) {
if (this.childs == null) {
this.childs = childrens;
}
else {
this.childs.retainAll(childrens);
this.childs.addAll(childrens);
}
}
}
Child Class Entity
package com.example.demo.model;
import java.util.Objects;
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 javax.persistence.UniqueConstraint;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter
#Setter
#NoArgsConstructor
#DynamicInsert
#DynamicUpdate
#Entity
#Table(name = "child", uniqueConstraints = {
#UniqueConstraint(columnNames = { "a", "b", "c", "parent_id" }) })
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String a;
private String b;
private String c;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parent_id")
private Parent parent;
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Child)) {
return false;
}
Child that = (Child) o;
return Objects.equals(getA(), that.getA()) && Objects.equals(getB(), that.getB())
&& Objects.equals(getC(), that.getC());
}
#Override
public int hashCode() {
return Objects.hash(getA(), getB(), getC());
}
}
Repository Class:
package com.example.demo.model;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
#Repository
public interface ParentRepository extends JpaRepository<Parent, Long> {
#Query("select p from Parent p join fetch p.childs where p.a = ?1")
Parent findByA(String a);
}
Main-Class with the business case:
package com.example.demo;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.util.Assert;
import com.example.demo.model.Child;
import com.example.demo.model.Parent;
import com.example.demo.model.ParentRepository;
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
#Autowired
ParentRepository repository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Child c1 = new Child();
c1.setA("a1");
c1.setB("b1");
c1.setC("c1");
Child c2 = new Child();
c2.setA("a2");
c2.setB("b2");
c2.setC("c2");
Parent p = new Parent();
p.addChild(c1);
p.addChild(c2);
p.setA("a");
repository.save(p);
// This works till now
// We will get the request for updating parent which might contain removal or addition of the child
Parent retrievedParent = repository.findByA("a");
retrievedParent.setB("b");
Child c4 = new Child();
c4.setA("a2");
c4.setB("b2");
c4.setC("c2");
Child c3 = new Child();
c3.setA("a3");
c3.setB("b3");
c3.setC("c3");
//If we know that c1 is removed and c3 is added we can use synchronize methods written in Parent
//As we don't know which are removed and which are added also as we won't get the id from request passing them
// directly to set to let hibernate handle it as equals and Hashcode is already written.
Set<Child> childrens = new HashSet<>();
childrens.add(c3);
childrens.add(c4);
retrievedParent.setChilds(childrens);
Parent persistedParent = repository.save(retrievedParent);
for (Child child : persistedParent.getChilds()) {
Assert.notNull(child.getParent(), "Parent must not be null");
//For child 3 it is failing
}
}
}
With above code, it is unable to set the parent id for child entity 4, if we print SQL logs we can observe that child with id 1 is deleted and child with id 3 is inserted which is expected.
As a workaround I am iterating all child entries and if the parent is not set then setting manually. I don't want this extra update statement.
Other approaches tried, removing all child entries using synchronized removeChild method and then add remaining one by one using synchronized addChild method . This is causing unique constraint failure exception.
What is required?
Setting the parent instead of workaround in the insert statement when it is executed.
The problem comes from this part:
Set<Child> childrens = new HashSet<>();
childrens.add(c3);
childrens.add(c4);
retrievedParent.setChilds(childrens);
You never need to rewrite a managed collection.
Now, based on your design:
If we know that c1 is removed and c3 is added we can use synchronize
methods written in Parent.
As we don't know which are removed and
which are added also as we won't get the id from request passing them
directly to set to let hibernate handle it as equals and Hashcode
is already written.
If the client sends you a collection of entries, you need to do the matching yourself, meaning that you need to:
add new elements
remove elements that are no longer needed
update the existing ones

JSON return nested arrays instead of objects [Spring boot + JPA + MySQL + REST]

ISSUE
Hello Guys please help me to solve this.
I ve started building a REST API and got a problem when testing the URLs that I've made. Example: when I send request to get the list of one object, the request work fine but the data syntax returned by JSON is ugly: I got in result nested arrays instead of one global array containing json Objects inside it. Check my code please, I have 2 entities now that one of them depend on the other, I used #OneToMany to make relationships between them and no error has occured. Thanks in advance.
SOLUTION
The problem is: my query was returning a list of lists by default, so I had to modify my query by adding a constructor call. check this links please: using new keyword in HQL query
Also I added #JsonIgnore annotation to ignore some properties in my entities to prevent their show. Now the data is shown as formatted as I want :D thanks for your help. Check the new result here
Update
Hello again, I realized recently, that is bad to use #JsonIgnore annotation to prevent some properties from being send in the Json response, and the best way to customize which properties to send is to use DTOs class. Thanks again kj007
Entity 1
import java.util.List;
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 lombok.Data;
#Data
#Table(name = "x_assureurs") // this is the table name in DB
#Entity(name = "Assureurs") // This tells Hibernate to make a table out of this class
public class Assureurs {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "n_assureur")
private String id;
#Column(name = "nom_assureur")
private String name;
#OneToMany(mappedBy="assureur",fetch = FetchType.LAZY)
private List<Contrats> contrats;
}
Entity 2
import javax.persistence.Column;
import javax.persistence.Entity;
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 lombok.Data;
#Data
#Table(name = "contrats") // this is the table name in DB
#Entity(name = "Contrats") // This tells Hibernate to make a table out of this class
public class Contrats {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "num_contrat")
private String num;
#Column(name = "nom_police")
private String nomPolice;
#ManyToOne
#JoinColumn(name = "courtier")
private Courtiers courtier;
#ManyToOne
#JoinColumn(name = "assureur")
private Assureurs assureur;
}
Repository
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import tn.igase.gestdoc.models.entities.Assureurs;
// This will be AUTO IMPLEMENTED by Spring into a Bean called assureurRepository
#Repository
public interface AssureurRepository extends JpaRepository<Assureurs, String> {
// CONSTANTS
String FIND_ALL_BY_CONTRATS = "SELECT DISTINCT(contrat.assureur.id) as n_assureur, assureur.name \n"
+ " FROM Contrats contrat \n" + " JOIN Assureurs assureur ON contrat.assureur.id = assureur.id ";
String BY_ONE_COURTIER = "WHERE contrat.courtier.id = :idCourtier";
// QUERIES
#Query(FIND_ALL_BY_CONTRATS)
Iterable<Assureurs> findAllByContrats();
#Query(FIND_ALL_BY_CONTRATS + BY_ONE_COURTIER)
Iterable<Object> findAllByContratsAndCourtier(#Param("idCourtier") int idCourtier);
}
Service
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tn.igase.gestdoc.daos.AssureurRepository;
import tn.igase.gestdoc.models.entities.Assureurs;
#Service
public class AssureurService {
#Autowired
AssureurRepository assureurRepository;
public Iterable<Assureurs> findAllByContrats() {
return assureurRepository.findAllByContrats();
}
}
Controller
import java.util.ArrayList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import tn.igase.gestdoc.models.entities.Assureurs;
import tn.igase.gestdoc.service.AssureurService;
import tn.igase.gestdoc.service.ContratService;
/**
*
* Assureur controller
*
* #author fre
*/
#RestController
#RequestMapping(path = "/api/assureurs")
public class AssureurController extends MainController {
#Autowired
private AssureurService assureurService;
/**
* Revert all assureurs that all have contrats
*
* #return list
*/
#RequestMapping(path = "/all", produces=MediaType.APPLICATION_JSON_VALUE)
public Iterable<Assureurs> getAll() {
// This returns a JSON or XML with the users
Iterable<Assureurs> assureurs = new ArrayList<>();
assureurs = assureurService.findAllByContrats();
return assureurs;
}
}
Result
Check the JSON data returned here
Your current HQL will return list of objects that’s why you are seeing result like this.
you can either return entity or ID(type) from a HQL or JPA named query..not projected/custom columns.
To order to achieve your list of object you can do it via couple of ways..
As HQL will retrun list of objects you can parse the object according to your need in your service class method.
#Query(FIND_ALL_BY_CONTRATS)
List<Object> findAllByContrats();
2. Use DTO (Which is best way to it)
STEP1: Create DTO for projected columns you want, make sure constructure meet the parameters required from hql ..for example..
#Data
public class AssureursDTO {
private Long n_assureur;
private String name;
public AssureursDTO(Long n_assureur, String name) {
this.n_assureur = n_assureur;
this.name = name;
}
}
STEP 2: define your HQL like this by passing full package path of DTO, use yours
String FIND_ALL_BY_CONTRATS = "SELECT DISTINCT new com.example.demomysql21.entity.AssureursDTO(assureur.id as n_assureur, assureur.name) \n"
+ " FROM Contrats contrat \n" + " JOIN Assureurs assureur ON contrat.assureur.id = assureur.id";
STEP 3: Now it will return you LIST
#Query(FIND_ALL_BY_CONTRATS)
List<AssureursDTO> findAllByContrats();

Does Class name matter during ORM process?

I have defined 3 classes as follows:
I tried to generate the schema in MySQL.
It won't succeed. But the wired thing is after I have renamed one class, CerifLink--->MyLink, it works.
Can anyone give a reasonable explanation?
package org.epos.grdb.jpa.entity;
import java.io.Serializable;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.ManyToOne;
#Entity
public class CerifLink implements Serializable {
/**
*
*/
private static final long serialVersionUID = -9162577962410473641L;
#Id
#ManyToOne
#JoinColumns(value = { #JoinColumn(referencedColumnName = "id"),
#JoinColumn(referencedColumnName = "cfid") }, foreignKey = #ForeignKey(value = ConstraintMode.CONSTRAINT, foreignKeyDefinition = "foreign key (`cfClassId`) references `cfClass` (`cfClassId`)"))
private Class clazz;
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
}
package org.epos.grdb.jpa.entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class MyEntity implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5185624925049306788L;
#Id
protected String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
package org.epos.grdb.jpa.entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
#Entity
public class Class implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1573828246619342971L;
#Id
protected String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Id
#ManyToOne
#JoinColumn(name = "cfid", referencedColumnName = "id")
private MyEntity classSchema;
public MyEntity getClassSchema() {
return classSchema;
}
public void setClassSchema(MyEntity classSchema) {
this.classSchema = classSchema;
}
}
But the wired thing is after I have renamed one class, CerifLink--->MyLink, it works.
Can anyone give a reasonable explanation?
Yes class name matter, you should check the NamingStrategy of Hibernate, you can modify it depending on the hibernate version you are using, but for what are you doing I guess you can use the annotation table and assign a specific name
#Table(name = "myname")

Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property

I need to make onetomany relationship but this error appears
mappedBy reference an unknown target entity property
this is parent Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property
package com.dating.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
#Entity
#Table(name="question")
public class PsyQuestions {
#Id
#GenericGenerator(name="autoGen" ,strategy="increment")
#GeneratedValue(generator="autoGen")
#Column(name="questionid")
private long psyQuestionId;
#Column(name="questiontext")
private String question;
#OneToMany(fetch = FetchType.LAZY,mappedBy="question")
private List<PsyOptions> productlist=new ArrayList<PsyOptions>();
public PsyQuestions() {
super();
}
public List<PsyOptions> getProductlist() {
return productlist;
}
public void setProductlist(List<PsyOptions> productlist) {
this.productlist = productlist;
}
public long getPsyQuestionId() {
return psyQuestionId;
}
public void setPsyQuestionId(long psyQuestionId) {
this.psyQuestionId = psyQuestionId;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
}
and this child class
package com.dating.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
#Entity
#Table(name="option")
public class PsyOptions {
#Id
#GenericGenerator(name="autoGen" ,strategy="increment")
#GeneratedValue(generator="autoGen")
#Column(name="optionid")
private long psyOptionId;
#Column(name="optiontext")
private String optionText;
#JoinColumn(name = "questionid")
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
PsyQuestions psyQuestions;
public PsyOptions() {
super();
}
public PsyQuestions getPsyQuestions() {
return psyQuestions;
}
public void setPsyQuestions(PsyQuestions psyQuestions) {
this.psyQuestions = psyQuestions;
}
public long getPsyOptionId() {
return psyOptionId;
}
public void setPsyOptionId(long psyOptionId) {
this.psyOptionId = psyOptionId;
}
public String getOptionText() {
return optionText;
}
public void setOptionText(String optionText) {
this.optionText = optionText;
}
}
You need to set the mappedBy attribute of the #OneToMany annotation to psyQuestions instead of question. The value of mappedBy attributes is the name of the class field on the other side of the relationship, in this case psyQuestions of the ManyToOne side of class PsyOptions.
public class PsyQuestions {
....
#OneToMany(fetch = FetchType.LAZY,mappedBy="psyQuestions")
private List<PsyOptions> productlist=new ArrayList<PsyOptions>();
....
I had the same issue because the mappedBy in the source entity was defined to "enrollment" (annotated with #OneToMany) but the corresponding property in the target entity was "bankEnrollment"; this is the property annotated with #ManyToOne.
After updating from enrollment to bankEnrollmentin the source entity, the exception went away (as expected_.
Lesson learnt: the mappedBy value (e.g. psyQuestions) should exist as a property name in the target entity.
Not sure if this is going to help anyone, it was a simple mistake caused this error on my config. Without realising I have had two different packages containing domain class files. Mapped member was in the other package while the application was only scanning single package path.
It was hard to figure out as they were different only by a single character e.g. org.abc.core.domains and org.abcs.core.domains.
Added other package to database #configuration scanners resolved the issue for me.
I just correct my mapping and it solved the problem.
...
#OneToMany(fetch = FetchType.LAZY,mappedBy="question")
...

Categories

Resources