Im creating an application in spring boot. I want to have multiple entity mapping for one table.
one entity retrieve the table without any join and the other entity retrieve the table with join.
This is the table schema:
book : {"id":1, "title":"math1", "author_id":1}
author: {"id":1, "name": "james"}
thank you.
Ive made it.
#MappedSuperclass
public abstract class BookBase<T extends BookBase> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false, updatable = false, name = "id")
private Integer id;
#Column(name = "title")
private String title;
public Integer getId() {
return this.id;
}
public T setId(Integer id) {
this.id = id;
return (T) this;
}
public String getTitle() {
return this.title;
}
public T setTitle(String title) {
this. title = title
return (T) this;
}
}
#Entity
#Table
public class Book extends BookBase<Book> {
#Column(name = "author_id")
private Integer authorId;
public Integer getAuthorId() {
return authorId;
}
public void setAuthorId(Integer authorId) {
this.authorId = authorId;
}
}
#Entity
#Table
public class BookJoinAuthor extends BookBase<BookJoinAuthor> {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "author_id")
private Author author;
public Author getAuthor() {
return author;
}
public void setAuthorId(Author author) {
this.author = author;
}
}
#Entity
#Table
public abstract class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false, updatable = false, name = "id")
private Integer id;
#Column(name = "name")
private String name;
public Integer getId() {
return this.id;
}
public T setId(Integer id) {
this.id = id;
return (T) this;
}
public String getName() {
return this.name;
}
public T setName(String name) {
this. name = name
return (T) this;
}
}
Related
I am referencing an Entity in another Entity class and getting this error. Below is sample code. I have these classes in the persistence.xml as well.
What is causing this issue? I am using Spring data JPA and Hibernate.
import javax.persistence.*;
#Entity
#Table(name = "users", schema = "university")
public class UsersEntity {
private long id;
#JoinColumn(name = "address_id", nullable = false)
private Address address;
#Id
#Column(name = "id")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
import javax.persistence.*;
#Entity
#Table(name = "address", schema = "university")
public class AddressEntity {
private long id;
private String street;
#Id
#Column(name = "id")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Basic
#Column(name = "street")
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
So try the following:
#Entity
#Table(name = "users", schema = "university")
public class UsersEntity {
private Long id;
private AddressEntity address;
#Id
#Column(name = "id")
public Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#OneToOne
#JoinColumn(name = "address_id", nullable = false)
public AddressEntity getAddress() {
return address;
}
public void setAddress(AddressEntity address) {
this.address = address;
}
}
#Entity
#Table(name = "address", schema = "university")
public class AddressEntity {
private Long id;
private String street;
#Id
#Column(name = "id")
public Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Column(name = "street")
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
Basically what I did was replace the long for Long. and added the #OneToOne
I removed the #Basic since its optional. I believe with that it should just work
You missed the type of relationship between entities. E.g. you can define one-directional #OneToOne as
//...
public class UsersEntity {
//..
#OneToOne(mappedBy = "ugdArea", fetch = FetchType.LAZY)
#JoinColumn(name = "address_id", nullable = false)
private Address address;
//...
}
I'm using Spring Boot,REST and JPA to build my application. In app, there are 2 entities with one to many relationship.
Entity 1 :
#Entity
#Table( name = "report")
#JsonIgnoreProperties(ignoreUnknown = true)
public class CustomReport {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "REPORT_SEQ")
#SequenceGenerator(sequenceName = "REPORT_SEQ", allocationSize = 1, name = "REPORT_SEQ")
private Long id;
private String name;
private Long createdBy;
private Timestamp lastModifiedTimestamp;
#OneToMany(mappedBy = "customReport", cascade = CascadeType.ALL)
private Set<CustomReportActivity> customReportActivitySet;
public Set<CustomReportActivity> getCustomReportActivitySet() {
return customReportActivitySet;
}
public void setCustomReportActivitySet(Set<CustomReportActivity> customReportActivitySet) {
this.customReportActivitySet = customReportActivitySet;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Long createdBy) {
this.createdBy = createdBy;
}
public Timestamp getLastModifiedTimestamp() {
return lastModifiedTimestamp;
}
public void setLastModifiedTimestamp(Timestamp lastModifiedTimestamp) {
this.lastModifiedTimestamp = lastModifiedTimestamp;
}
}
Entity 2:
#Entity
#Table( name = "report_activity")
public class CustomReportActivity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "REPORT_ACTIVITY_SEQ")
#SequenceGenerator(sequenceName = "REPORT_ACTIVITY_SEQ", allocationSize = 1, name = "REPORT_ACTIVITY_SEQ")
private Long id;
String activityName;
#ManyToOne
#JoinColumn( name="report_id" )
#JsonBackReference
private CustomReport customReport;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getActivityName() {
return activityName;
}
public void setActivityName(String activityName) {
this.activityName = activityName;
}
public CustomReport getCustomReport() {
return customReport;
}
public void setCustomReport(CustomReport customReport) {
this.customReport = customReport;
}
}
And my request JSON is as follows :
{
"name": "test report",
"createdBy" : 129,
"customReportActivitySet": [
{"activityName":"a"},
{"activityName":"b"},
{"activityName":"c"},
{"activityName":"d"},
{"activityName":"e"}
]
}
I want to save both entities in one shot. I've implemented the save functionality in following way:
#RequestMapping(value="/save", method = RequestMethod.POST)
public ResponseEntity<?> addReport(#RequestBody CustomReport customReport) {
return new ResponseEntity<>(customReportService.createCustomReport(customReport), HttpStatus.CREATED);
}
CustomReportService method:
public CustomReport createCustomReport(CustomReport customReport) {
return customReportRepository.save(customReport);
}
CustomRepository:
public interface CustomReportRepository extends CrudRepository<CustomReport, Long> {
}
But I'm getting the constraint violation exception with this:
java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot
insert NULL into ("REPORT_ACTIVITY"."REPORT_ID")
Is it possible to save both entities in one save operation?
Please help!
You would have to add a small piece of code which would populate each CustomReportActivity within the CustomReport instance. Only then the persistence provide can successfully perform the cascade save operation:
public CustomReport createCustomReport(CustomReport customReport) {
customReport.getCustomReportActivitySet.forEach((activity) -> {
activity.setCustomReport(customReport);
});
return customReportRepository.save(customReport);
}
The bottom line is that the dependencies have to be set on both sides of the relationship.
Try this sample, in my case it worked as expected, child entities are saved automatically in a single save operation with creating relations to the parent entity:
#Entity
public class Parent {
#Id
private Long id;
#JoinColumn(name = "parentId")
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Child> children;
}
#Entity
public class Child {
#Id
private Long id;
private Long parentId;
}
There are 3 tables. There is the variable of "relatedCameraSet" need to order by "camera.name" using SQL, but the field of "camera.name" is not in table of "RelatedCamera", is in the outer joined table of "Camera". The following annotation of #OrderBy doesn't work.
#Entity
#Table(name = "MICRO_MAP")
public class MicroMap { //main table
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private Long id;
#Column(name = "NAME", length = 32, nullable = false)
private String name;
#OneToMany(mappedBy="mapId",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#OrderBy("camera.name") //OrderBy the field of "name" in Camera table
private Set<RelatedCamera> relatedCameraSet;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<RelatedCamera> getRelatedCameraSet() {
return relatedCameraSet;
}
public void setRelatedCameraSet(Set<RelatedCamera> relatedCameraSet) {
this.relatedCameraSet = relatedCameraSet;
}
}
#Entity
#Table(name = "RELATED_CAMERA")
public class RelatedCamera {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private Long id;
#Column(name = "MAP_ID")
private String mapId;
#ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name = "CAMERA_ID", referencedColumnName="id",nullable = true)
private Camera camera;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getMapId() {
return mapId;
}
public void setMapId(String mapId) {
this.mapId = mapId;
}
public Camera getCamera() {
return camera;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
}
#Entity
#Table(name = "CAMERA")
public class Camera {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
How to write #OrderBy annotation in order to sort collection by camera name using SQL?
Thanks alot!
I researched your problem and found that from the API docs:
Hibernate JPA 2.1 API
The property or field name must correspond to that of a persistent property or field of the associated class or embedded class within it.
that's why it is not possible do it by just OrderBy, you can order only by camera_id column from MicroMap class.
What you want can be done by #Sort and implementing Comparable interface or by specifying a Comparator:
Annotation Type Sort
Now i realize it's impossible to do by #OrderBy, and i think it's not efficient to to do by implementing Comparable interface or specifying a Comparator, because it will take two steps to get job done, the step one is query from DB, the step two is sorting in memory. It's efficient to get sorted collection just by SQL.
In order to get high efficiency, i have to change the class of MicroMap as following:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "MICRO_MAP")
public class MicroMap {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private Long id;
#Column(name = "NAME", length = 32, nullable = false)
private String name;
// #OneToMany(mappedBy="mapId",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
// #OrderBy("camera.name") //OrderBy the field of "name" in Camera table, But JPA doesn't support
// private Set<RelatedCamera> relatedCameraSet;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and add a method in DAO or Service class.
public List<RelatedCamera> getRelatedCamera(Long mapId) {
Session session = sessionFactory.getCurrentSession();
List<RelatedCamera> list = session.createQuery(" from RelatedCamera where mapId="+mapId+" order by camera.name").list();
return list;
}
Here is my entitiy:
#Entity
#Table(name = "remind")
public class Remind {
#Id
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
private long id;
#Column(name = "title", nullable = false, length = 50)
private String title;
#Column(name = "remind_date", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date remindDate;
public Remind() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getRemindDate() {
return remindDate;
}
public void setRemindDate(Date remindDate) {
this.remindDate = remindDate;
}
//---Dependency---->
#OneToOne(optional = false)
#JoinColumn(name="id_user", unique = true, nullable = true, updatable = false)
private Users user;
public void setUser(Users user) {
this.user = user;
}
public Users getUser() {
return user;
}
}
And here is another entity:
#Entity
#Table(name = "users")
public class Users {
#Id
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
private long id;
#Column(name = "name", nullable = false, length = 50)
private String name;
public Users() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String title) {
this.name = name;
}
//---Dependency---->
#OneToOne(optional = false, mappedBy="user")
public Remind remind;
public Remind getRemind() {
return remind;
}
public void setRemind(Remind remind) {
this.remind = remind;
}
Here is diagram that Idea shows me:
But when I use in RemindController.class:
#RequestMapping(value = "/get", method = RequestMethod.GET)
#ResponseBody
public List<Remind> getReminder() {
List<Remind> list = remindRepository.findAll();
return list;
}
I have this result... infinity loop:
What am I do wrong? It seems like diagram is ok. Help me please(
It's not jpa problem. It's a problem with serializing your entity to json. You need to explicitly mark the relation as bidirectional so the serializer can skip one end.
For Spring I'm assuming you're using jackson.
Take a look at answers to this question.
I have two classes Task and TaskComponents. TaskComponents contains a task mapped by a Task_ID. I am trying to select a task joined to the TaskComponents table. I have tried many different SQL statements but all of them come back with QuerySyntaxException Path Expected to join.
Task POJO
#Entity
#Table(name = "task")
public class Task implements Serializable {
#Id
#Column(name = "ID")
private int Id;
#Column(name = "Name")
private String name;
#Column(name = "Description")
private String description;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "Site_ID")
private Site site;
public int getId() {
return Id;
}
public void setId(int Id) {
this.Id = Id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Site getSite() {
return site;
}
public void setSite(Site site) {
this.site = site;
}
}
TaskComponents POJO
#Entity
#Table(name = "taskcomponents")
public class TaskComponents implements Serializable {
#Id
#Column(name = "ID")
private int Id;
#Column(name = "VersionName")
private String versionName;
#Column(name = "Live", nullable = false)
#Type(type = "org.hibernate.type.NumericBooleanType")
private boolean live;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "Task_ID")
private Task task;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "Group_ID")
private GroupDB group;
public int getId() {
return Id;
}
public void setId(int Id) {
this.Id = Id;
}
public String getVersionName() {
return versionName;
}
public void setVersionName(String versionName) {
this.versionName = versionName;
}
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
public Task getTask() {
return task;
}
public void setTask(Task task) {
this.task = task;
}
public GroupDB getGroup() {
return group;
}
public void setGroup(GroupDB group) {
this.group = group;
}
}
And my attempted queries.
Query query = session.createQuery("SELECT t FROM Task t INNER JOIN TaskComponents tc ON t.Id=tc.task.Id");
Query query = session.createQuery("SELECT t FROM Task t INNER JOIN TaskComponents tc ON t=tc.task");
You shouldn't use explicitely ON to define the join, Hibernate will infer it from the mapping, just write you query as
SELECT tc.task FROM TaskComponents tc INNER JOIN tc.task
this is what is referred to behind your error message Path expected for join the query expects a property defined path from one entity to another