i would use a ModelBase with an ID and a Timestamp for every class/entity. But when i user the Long type for the Primary Key in the JPARepository<> interface i get the message
Not an entity: class java.lang.Long
Code:
#MappedSuperclass
public class ModelBase implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false, columnDefinition = "datetime")
private Date lastModified;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getLastModified() {
return lastModified;
}
public void setLastModified(Date lastModified) {
this.lastModified = lastModified;
}
#PreUpdate
#PrePersist
public void updateLastModified() {
lastModified = new Date();
}
}
Modelclass inheritanced from Modelbase
#Entity
#Table(name = "Name")
public class Name extends ModelBase implements Serializable {}
Repo
public interface NameRepository extends JpaRepository<Long, Name>{}
what am i doing wrong?
thanks
It's backwards:
JpaRepository<Name, Long>
First the entity, then the ID. Check JPARepository javadoc.
Related
I am mapping Entities in Hibernate with JPA and Spring Data and when I run application I get
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [id] on this ManagedType [p.s.t..entity.BaseEntity]
at org.hibernate.metamodel.internal.AbstractManagedType.checkNotNull(AbstractManagedType.java:128) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.metamodel.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:113) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.metamodel.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:111) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:633) ~[spring-data-jpa-2.1.11.RELEASE.jar:2.1.11.RELEASE]
at org.springframework.data.jpa.repository.query.JpaQueryCreator.complete(JpaQueryCreator.java:175) ~[spring-data-jpa-2.1.11.RELEASE.jar:2.1.11.RELEASE]
I have a superclass BaseEntity:
#MappedSuperclass
#Getter
#Setter
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue
private Long Id;
private String uuid = UUID.randomUUID().toString();
#Override
public boolean equals(Object that) {
return this == that ||
that instanceof BaseEntity && Objects.equals(uuid, ((BaseEntity) that).uuid);
}
#Override
public int hashCode() {
return Objects.hash(uuid);
}
}
Regular class Task, which extends the BaseClass
#Getter
#Setter
#Table(name = "task")
#Entity
#NoArgsConstructor
#NamedEntityGraph(
name = "Task.detail",
attributeNodes = {
#NamedAttributeNode("attachments"),
#NamedAttributeNode("tags")
}
)
public class Task extends BaseEntity {
private String title;
private String description;
private LocalDateTime createdAt;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "task_id")
private Set<Attachment> attachments = new HashSet<>();
#ManyToMany
#JoinTable(
name = "tags_tasks",
joinColumns = #JoinColumn(name = "task_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id")
)
private Set<Tag> tags = new HashSet<>();
public Task(String title, String description, LocalDateTime createdAt) {
this.title = title;
this.description = description;
this.createdAt = createdAt;
}
public void addAttachment(String filename, String comment) {
attachments.add(new Attachment(filename, comment));
}
public Set<Attachment> getAttachments() {
return attachments;
}
public void addTag(Tag tag) {
tags.add(tag);
}
public void removeTag(Tag tag) {
tags.remove(tag);
}
}
TaskView for JPA query projection:
public interface TaskView {
Long getId();
String getUuid();
String getTitle();
String getDescription();
LocalDateTime getCreatedAt();
}
And JpaRepository interface:
interface TasksCrudRepository extends JpaRepository<Task, Long> {
#EntityGraph(value = "Task.detail", type = EntityGraphType.LOAD)
List<Task> findAll();
List<TaskView> findAllProjectedBy();
}
The last method - findAllProjectedBy() - in the TaskCrudRepository causes the exception pasted at the begnining of this post.
When I remove getId() method from TaskView it starts, but then I am not able to display the id of the Task in the projection.
So the question is what I am missing in this whole classes structure?
I am using:
Spring Boot 2.1.9.RELEASE
Java 11
Hibernate Core 5.3.12.FINAL
JPA 2.2
There is a typo in BaseEntity when defining ID field. Should be camelcase id instead of Id.
#MappedSuperclass
#Getter
#Setter
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
private String uuid = UUID.randomUUID().toString();
#Override
public boolean equals(Object that) {
return this == that ||
that instanceof BaseEntity && Objects.equals(uuid, ((BaseEntity) that).uuid);
}
#Override
public int hashCode() {
return Objects.hash(uuid);
}
}
I create a base class for all entities to every new entity class extend it.
in base class I use annotation for orm, below code:
public class BaseEntity<I> implements IBaseEntity<I> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", nullable = false)
private I id;
public I getId() {
return id;
}
public void setId(I id) {
this.id = id;
}}
then I create a class as below:
#SuppressWarnings("serial")
#Entity
#Table(name = FoodEntity.TABLE_NAME, schema = "public")
public class FoodEntity extends BaseEntity<Long> {
public static final String TABLE_NAME = "T_Food";
#Column(name = "NAME", unique = true, nullable = false, length = 500)
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}}
but, when I execute a sample to test my classes, I get below exception:
Initial SessionFactory creation failed.org.hibernate.AnnotationException: No identifier specified for entity: FoodEntity
Exception in thread "main" java.lang.ExceptionInInitializerError
at ir.msr.projects.crawler.HibernateUtil.buildSessionFactory(HibernateUtil.java:31)
at ir.msr.projects.crawler.HibernateUtil.<clinit>(HibernateUtil.java:14)
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: FoodEntity
at org.hibernate.cfg.InheritanceState.determineDefaultAccessType(InheritanceState.java:266)
at org.hibernate.cfg.InheritanceState.getElementsToProcess(InheritanceState.java:211)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:731)
at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:249)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:222)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:265)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:83)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:418)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:87)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:691)
I found my answer, I have to use #MappedSuperclass for BaseEntity Class(Parent Class).
Try to create BaseEntity abstract and anotated Id put to your main class, or use annotation #MappedSuperclass
#MappedSuperclass
public class BaseEntity<I> implements IBaseEntity<I> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", nullable = false)
private I id;
public I getId() {
return id;
}
public void setId(I id) {
this.id = id;
}
}
or
public abstract class BaseEntity<ID extends Serializable, T extends BaseEntity<ID, T>> {
protected ID id;
public abstract ID getId();
public void setId(ID id) {
this.id = id;
}
}
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;
}
I'm trying to insert a record with composite primary key, but at the time of saving a new record I get this message:
e = (org.springframework.orm.jpa.JpaSystemException)
org.springframework.orm.jpa.JpaSystemException: Could not set field
value [POST_INSERT_INDICATOR] value by reflection...
#Getter
#Setter
#Entity
#EqualsAndHashCode
#Table(name = "produto")
#IdClass(ProdutoId.class)
public class Produto implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_produto")
private Long idProduto;
#Id
#Column(name = "oficina", insertable = false, updatable = false)
private Long idOficina;
#ManyToOne
#JoinColumn(name = "oficina")
private Oficina oficina;
}
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#Data
public class ProdutoId implements Serializable {
public Long idProduto;
public Long idOficina;
}
#Repository
public interface ProdutoRepository extends JpaRepository<Produto, ProdutoId> {}
Has anyone ever seen a bug like this?
To create composite primary key you can refer this example,
#Entity
#Table(name="testuserrole")
public class UserRole{
#EmbeddedId
private UserRoleId id = new UserRoleId();
public UserRoleId getId() {
return id;
}
public void setId(UserRoleId id) {
this.id = id;
}
#Transient
public long getUserId() {
return id.userId;
}
public void setUserId(long userId) {
id.userId=userId;
}
#Transient
public long getRoleId() {
return id.roleId;
}
public void setRoleId(long roleId) {
id.roleId=roleId;
}
}
#Embeddable
class UserRoleId implements Serializable {
#Column(name = "user_id")
public long userId;
#Column(name = "role_id")
public long roleId;
}
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;
}