Why hibernate ignoring my updates? - java

I created abstract generic #MappedSuperclass. This is working fine with select, insert queries. But hibernate don't generate update queries. I don't understand what's wrong with generic class.
#MappedSuperclass
#Cacheable
public abstract class Category<T extends Category> {
private Integer id;
private String name;
private T parent;
private List<T> children;
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() {
return id;
}
//omitted
}
#Entity
#Table(name = "staticPageCategory")
public class StaticPageCategory extends Category<StaticPageCategory> implements Serializable {
#ManyToOne
#Override
public StaticPageCategory getParent() {
return super.getParent();
}
#OrderBy("ordering asc, name asc")
#OneToMany(mappedBy = "parent")
#Override
public List<StaticPageCategory> getChildren() {
return super.getChildren();
}
}
Question is why hibernate ignoring getCurrentSession().update(myCategory);

Related

jpa Inheritance for relational entities

i'm using a library which have some entity and relations
like this :
public class BaseEntity1{
private Long id;
private List<BaseEntity2> baseEntity2List;
}
public class BaseEntity2{
private Long id;
private String title;
}
Entities have no jpa annotation's
i need to persist them into database
so i extend them and Override getter's to add jpa annotations
like this :
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class ChildEntity1 extends BaseEntity1{
#Override
#Id
public Long getId(){
..
}
#Override
#OneToMany
public List<BaseEntity2> getBaseEntity2List(){
..
}
}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class ChildEntity2 extends BaseEntity2{
#Override
#Id
public Long getId(){
..
}
#Override
#Column
public String getName(){
..
}
}
my problem is that OneToMany relation , i have no idea to replace BaseEntity2 to ChildEntity2
Does anyone have any idea ?

Unable to save Entity with OneToMany mapping using JPA

I have Order and OrderLineItem with One-to-Many relationship. When I am saving the Order, I am getting ConstraintViolationException. Below is the code snippet.
#Entity
#Table(name = "orders")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Order extends AuditModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_id")
#JsonProperty("order_id")
private long orderId;
private double price;
#OneToMany(mappedBy = "order",
fetch = EAGER,
cascade = CascadeType.ALL )
private Set<OrderLineItem> orderLineItems = new HashSet<>();
// scaffolding code
public void addOrderLineItem(OrderLineItem orderLineItem){
this.orderLineItems.add(orderLineItem);
orderLineItem.setOrder(this);
}
... setters, getters, toString, equals and hashcode methods
}
In OrderLineItem class
#Entity
#Table(name = "order_line_item")
#JsonIgnoreProperties(ignoreUnknown = true)
public class OrderLineItem extends AuditModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private double price;
#ManyToOne()
#JoinColumn(name = "order_id", nullable = false)
#JsonIgnore
private Order order;
public OrderLineItem(String name, double price){
this.name = name;
this.price = price;
}
public OrderLineItem(){}
... setters, getters, toString, equals and hashcode methods
}
OrderServiceImple class
#Service
public class OrderServiceImpl implements OrderService {
#Autowired
private OrderRepository orderRepository;
#Override
public Order createOrder(Order order) {
System.out.println("Inside the save method of Order service .... :: ");
System.out.println(order);
return this.orderRepository.save(order);
}
...
}
OrderRepository
#Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findAll();
}
I am using OrderRepository and OrderItemRepository JPA interfaces.
The post request
{
"price": 4500,
"orderLineItems": [
{
"name": "new Order Item",
"price": 4000
}
]
}
Error:
java.sql.SQLIntegrityConstraintViolationException: Column 'order_id' cannot be null
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117) ~[mysql-connector-java-8.0.19.jar:8.0.19]
Where am I going wrong?
JPA didn't find Order inside orderLineItems thats why order_id set as null. To save child with parent in the bidirectional relationship set parent in child entity also to sync both side.
public Order createOrder(Order order) {
for(OrderLineItem orderLineItem : order.getOrderLineItems()) {
orderLineItem.setOrder(order);
}
return this.orderRepository.save(order);
}

Unable to locate Attribute with the given name - Spring Data JPA Projections, Hibernate and BaseEntity

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);
}
}

How to inherit #Id from mapped superclass?

I want to create a primary composite key and use an #Id field from a parent class. But it does not work. Why?
#MappedSuperclass
static abstract class SuperEntity {
#Id
private Long id;
}
#Entity
#IdClass(SuperPK.class)
public static class ChildEntity extends SuperEntity {
#Id
private String lang;
}
public class SuperPK {
public SuperPK(Long id, String lang) {
//...
}
}
Result: Property of #IdClass not found in entity ChildEntity: id
I found an open issue regarding this bug.
One of the comments states to override the getters for the ID properties as a workaround.
#Entity
#IdClass(SuperPK.class)
public static class ChildEntity extends SuperEntity {
#Id
private String lang;
#Override #Id
public Long getId() {
return super.getId();
}
}

OpenJPA OneToMany and composite key in parent and child table

I have tables with composited primary key.
Server(key=ServerId)
ServerId|Name
1 |server1
2 |server2
ParentObj(key=ServerId+Code)
ServerId|Code |Title
1 |code1|value1
1 |code2|value2
2 |code1|Value2b
ChildObj(key=ServerId+Code+Name)
ServerId|Code |Name |Value
1 |code1|prop1|val1
1 |code1|prop2|val2
1 |code2|prop1|val1b
2 |code1|prop3|val3
This is Java beans I have.
#Entity #Table(name="ParentObj") #Access(AccessType.FIELD)
#IdClass(value=ParentObj.PK.class)
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement
public class ParentObj {
#Id private long serverId;
#Id private String code;
private String title;
public long getServerId() { return serverId; }
public String getCode() { return code; }
public String getTitle() { return title; }
public static class PK implements Serializable {
private static final long serialVersionUID = 1L;
private long serverId;
private String code;
public long getServerId() { return serverId; }
public void setServerId(long id) { serverId=id; }
public String getCode() { return code; }
public void setCode(String code) { this.code=code; }
}
}
#Entity #Table(name="ChildObj") #Access(AccessType.FIELD)
#IdClass(value=ChildObj.PK.class)
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement
public class ChildObj {
#Id private long serverId;
#Id private String code;
#Id private String name;
private String value;
// public getter+setters for each field
public static class PK implements Serializable {
private static final long serialVersionUID = 1L;
private long serverId;
private String code;
private String name;
public long getServerId() { return serverId; }
public void setServerId(long id) { serverId=id; }
public String getCode() { return code; }
public void setCode(String code) { this.code=code; }
public String getName() { return name; }
public void setName(String name) { this.name=name; }
}
}
I have been trying "everything" to create OneToMany mapping(ParentObj->ChildObj) but nothing seem to work. I don't need ManyToOne(ParentObj<-ChildObj) link but that's ok if one must be defined.
This is a legacy database so I cannot insert an auto_increment identity column or create extra join table between parent and childs.
This annotation is conceptually what I want but multiple join columns is not accepted by OpenJPA2.x library.
// from parent to zero or more childs
#OneToMany(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="server_id", referencedColumnName="server_id"),
#JoinColumn(name="code", referencedColumnName="code")
})
private List<ChildObj> properties;
Edit, answer
OneToMany, ManyToOne and EmbeddedId annotations works. I have only tried reading existing rows but its fine for now. Later I try update+insert+delete tasks.
public class ParentObj {
#EmbeddedId ParentObj.PK pk;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="parent", orphanRemoval=true)
private List<ChildObj> childs;
public PK getPK() { return pk; }
public void setPK(PK pk) { this.pk=pk; }
public List<ChildObj> getChilds() { return childs; }
...
#Embeddable #Access(AccessType.FIELD)
public static class PK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(nullable=false) private long serverId;
#Column(nullable=false) private String code;
..getters+setters+hashCode+equals functions
}
}
public class ChildObj {
#EmbeddedId ChildObj.PK pk;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST, optional=false)
#JoinColumns({
#JoinColumn(name="serverid", referencedColumnName="serverid", nullable=false),
#JoinColumn(name="code", referencedColumnName="code", nullable=false)
})
private ParentObj parent;
public PK getPK() { return pk; }
public void setPK(PK pk) { this.pk=pk; }
public long getServerId() { return pk.getServerId(); }
public String getCode() { return pk.getCode(); }
public String getName() { return pk.getName(); }
...
#Embeddable #Access(AccessType.FIELD)
public static class PK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(nullable=false) private long serverId;
#Column(nullable=false) private String code;
#Column(nullable=false) private String name;
..getters+setters+hashCode+equals functions
}
}
The easiest way to do this is to create an association from ChildObj to ParentObj similar to the following:
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumns({
#JoinColumn(name = "serverId", referencedColumnName = "serverId"),
#JoinColumn(name = "code", referencedColumnName = "code")})
private ParentObj parentObj;
and then define the #OneToMany association in ParentObj like this:
#OneToMany(mappedBy = "parentObj", fetch=FetchType.LAZY)
private List<ChildObj> children;
I would also recommend that you define your composite keys as #Embeddable classes, used as #EmbeddedId references in the Entities. These embeddable PK classes should be separate classes (not inner classes), as you will use them separately to query the related Entities, and serialisation of inner classes can cause problems

Categories