Currently I define the tables the following way:
The example is in Kotlin, but you can answer in Java if you want.
#Entity
data class Task(#Id #GeneratedValue(strategy = GenerationType.SEQUENCE)
var id: Int = 0,
#ManyToOne(optional = false) #OnDelete(action = OnDeleteAction.CASCADE)
var exercise: Exercise = Exercise(),
#Column(nullable = false)
var name: String = "")
#Entity
data class Exercise(#Id #GeneratedValue(strategy = GenerationType.SEQUENCE)
var id: Int = 0,
#Column(nullable = false)
var count: Int = 0)
Using this example all tables are using the same sequence: hibernate_sequence.
If I want to configure it e.g. setting a custom allocationSize, then I must define it in every table, am I right?
#SequenceGenerator(name = "task_seq", sequenceName = "task_seq", allocationSize = 100)
Is there a Bean or anything else? Because I like the idea of using the same sequence for all tables.
You can have an abstract base class with Id like :
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
#MappedSuperclass
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
and every entity will extend this.
Related
How to put in order hibernate and database sequence generation?
My entity:
package kz.open.sankaz.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
#Entity
#Table(name = "SEC_ROLE")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class SecRole extends AbstractEntity implements GrantedAuthority {
#Id
#GeneratedValue(generator = "SEC_ROLE_SEQ", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(allocationSize = 1, sequenceName = "SEC_ROLE_ID_SEQ", name = "SEC_ROLE_ID")
private Long id;
#Column(name = "NAME", nullable = false, unique = true)
private String name;
#Override
public String getAuthority() {
return getName();
}
}
I wrote that "allocationSize" is 1. But Hibernate generates wrong query:
Hibernate: create sequence public.sec_role_seq start 1 increment 50
It makes a problem not only while you are inserting a new data, it also makes problem when you are running database migration queries. For example, I wrote the next line:
create sequence public.sec_role_seq start 1 increment 1;
and Hibernate conflicts with it:
The increment size of the [SEC_ROLE_SEQ] sequence is set to [50] in the entity mapping while the associated database sequence increment size is [1]
How to solve it? Please, help!
I solved it changing generator in the following way:
#Entity
#Table(name = "SEC_ROLE")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class SecRole extends AbstractEntity implements GrantedAuthority {
#Id
#GeneratedValue(generator = "SEC_ROLE_SEQ", strategy = GenerationType.SEQUENCE)
#GenericGenerator(
name = "SEC_ROLE_SEQ",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "SEC_ROLE_SEQ"),
#Parameter(name = "initial_value", value = "1"),
#Parameter(name = "increment_size", value = "1")
}
)
private Long id;
#Column(name = "NAME", nullable = false, unique = true)
private String name;
#Override
public String getAuthority() {
return getName();
}
}
I want to save a record and its child list with spring boot through postman in a One-to-Many relationship. The child list is saved but they don't take the Id of the parent automatically. How can i force the child to automatically take the id of the parent in Post Request In Postman?
Parent Class
package fdsa.edu.pnu.Model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Data
#Table(name = "Concours")
public class Concours implements Serializable {
#Column(name = "ID", nullable = false, length = 10)
#Id
#GeneratedValue(generator = "PNU_CONCOURS_ID_GENERATOR")
#org.hibernate.annotations.GenericGenerator(name = "PNU_CONCOURS_ID_GENERATOR", strategy = "native")
private Integer id;
#Column(name = "DateDebut", nullable = true)
#Temporal(TemporalType.DATE)
private java.util.Date DateDebut;
#Column(name = "DateFin", nullable = true)
#Temporal(TemporalType.DATE)
private java.util.Date DateFin;
#Column(name = "Description", nullable = true, length = 255)
private String description;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "concours",
cascade = CascadeType.ALL,targetEntity = fdsa.edu.pnu.Model.PlannificationConcours.class)
private List<PlannificationConcours> plannificationConcourses;
}
Child Class
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "PlannificationConcours")
public class PlannificationConcours implements Serializable {
#Column(name = "ID", nullable = false, length = 10)
#Id
#GeneratedValue(generator = "PNU_PLANNIFICATIONCONCOURS_ID_GENERATOR")
#org.hibernate.annotations.GenericGenerator(name = "PNU_PLANNIFICATIONCONCOURS_ID_GENERATOR", strategy = "native")
private int id;
#ManyToOne(targetEntity = fdsa.edu.pnu.Model.Concours.class, fetch = FetchType.LAZY)
#JoinColumns(value = {#JoinColumn(name = "ConcoursID", referencedColumnName = "ID")}, foreignKey = #ForeignKey(name = "ConcoursPlannificationConCours"))
private Concours concours;
#Column(name = "`Date`", nullable = true)
#Temporal(TemporalType.DATE)
private java.util.Date Date;
#Column(name = "Quotation", nullable = true, length = 10)
private double quotation;
#Column(name = "NoteDePassage", nullable = true, length = 10)
private double noteDePassage;
}```
Screen Shote where the Id of the parent is null
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/LlnhP.png
There are 2 ways to reach it. And this is the minimal setting to do it:
Unidirectional
#Entity
#NoArgsConstructor
#Getter
#Setter
public class Concours {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "concours_id") // this line will play the role that passes the parent's id to its children
private List<PlannificationConcours> plannificationConcourses = new ArrayList<>();
}
#Entity
#NoArgsConstructor
#Getter
#Setter
public class PlannificationConcours {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
#Test
void saveConcours() {
Concours concours = new Concours();
concours.setPlannificationConcourses(List.of(new PlannificationConcours(), new PlannificationConcours()));
this.concoursRepository.save(concours);
}
This's all you need to propagate the parent's id. But the child won't have the reference to its parent by this way.
Bidirectional
#Entity
#NoArgsConstructor
#Getter
public class Concours {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(mappedBy = "concours" ,cascade = CascadeType.ALL) // mappedBy will create a bidirectional relation for us
private List<PlannificationConcours> plannificationConcourses = new ArrayList<>();
public void addPlannificationConcours(PlannificationConcours child) {
child.setConcours(this); // and don't forget to set the parent instance to the child
this.plannificationConcourses.add(child);
}
}
#Entity
#NoArgsConstructor
#Getter
#Setter
public class PlannificationConcours {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
private Concours concours;
}
#Test
void saveConcours() {
Concours concours = new Concours();
concours.addPlannificationConcours(new PlannificationConcours());
concours.addPlannificationConcours(new PlannificationConcours());
this.concoursRepository.save(concours);
}
I am making a Spring web service to learn more about it and I am currently mapping the database. I have a table that has a composite ID, where one of the ID's is a foreign key to another table (ManytoOne).
Creditors
Creditor_Invoices
ID
Creditor_ID
name
Invoice_ID
As anywhere you buy something they use their own way of making ID's it has a composite ID like this.
My Current code:
Serializable class CInvoiceId:
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import java.io.Serializable;
import java.util.Objects;
#Embeddable
public class CInvoiceId implements Serializable {
#ManyToOne
#JoinColumn(name = "creditors_id", nullable = false)
private Creditor cInvoiceCreditorId;
#Column(name = "invoice_id", nullable = false)
private String cInvoiceId;
public CInvoiceId(Creditor creditor, String cInvoiceId){
this.cInvoiceCreditorId = creditor;
this.cInvoiceId = cInvoiceId;
}
//Setters, Getters, Equals and Hash
}
My Creditor class
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "creditors")
public class Creditor {
#Id
#GeneratedValue
#Column(name = "id")
private int creditorId;
#Column(name = "name",nullable = false)
private String creditorName;
#OneToMany(mappedBy = "cInvoiceCreditorId")
private List<CInvoice> cInvoices;
}
My CInvoice class:
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
#Entity
#Table(name = "c_invoices")
public class CInvoice {
#EmbeddedId
private CInvoiceId cInvoiceID;
}
When I start it to try and test it I get the error that it can not find the mapped by from the creditor class, but I don't know what I should map it to as the ID is now made in the CInvoiceId class. What should it be?
Regards
Dany
You can use "derived identities" to map these classes:
Creditor:
#Entity
#Table(name = "creditors")
public class Creditor {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name",nullable = false)
private String name;
#OneToMany(mappedBy = "creditor")
private List<CInvoice> invoices;
}
CInvoiceId:
#Embeddable
public class CInvoiceId implements Serializable {
#Column(name = "invoice_id", nullable = false)
private String invoiceID;
private int creditorID; // corresponds to PK type of Creditor
// ...
}
CInvoice:
#Entity
#Table(name = "c_invoices")
public class CInvoice {
#EmbeddedId
private CInvoiceId id;
#MapsId("creditorID") // maps creditorID attribute of embedded id
#ManyToOne
#JoinColumn(name = "creditors_id", nullable = false)
Creditor creditor;
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
I have this error in my eclipse editor This class has a composite primary key. It must use an ID class. , but the class does not has a composite primary key, because the id is a Long
#SuppressWarnings("serial")
#Entity
#Table(name = "T_PRODUCT")
#SequenceGenerator(name = "seqPRODUCT", sequenceName = "SEQ_PRODUCT")
public class Product extends ItemBase implements java.io.Serializable {
#Id
private Long id;
#Id
#Column(name = "ID", unique = true, nullable = false, precision = 38, scale = 0)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqPRODUCT")
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
..
}
Hibernate is seeing both annotations on the property and on the getter-method and therefore assumes that you are using a composite key.
Is there a way to specify distinct sequences for each table in Hibernate, if the ID is defined on a mapped superclass?
All entities in our application extend a superclass called DataObject like this:
#MappedSuperclass
public abstract class DataObject implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id")
private int id;
}
#Entity
#Table(name = "entity_a")
public class EntityA extends DataObject { ... }
#Entity
#Table(name = "entity_b")
public class EntityB extends DataObject { ... }
This causes all entities to use a shared sequence, the default hibernate_sequence.
What I would like to do is use a separate sequence for each entity, for example entity_a_sequence and entity_b_sequence in the example above. If the ID were specified on the subclasses then I could use the #SequenceGenerator annotation to specify a sequence for each entity, but in this case the ID is on the superclass. Given that ID is in the superclass, is there a way I can use a separate sequence for each entity — and if so, how?
(We are using PostgreSQL 8.3, in case that's relevant)
Have you tried doing it this way ?
#MappedSuperclass
public abstract class DataObject implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idgen")
#Column(name = "id")
private int id;
}
#Entity
#SequenceGenerator(initialValue = 1, name = "idgen", sequenceName = "entityaseq")
#Table(name = "entity_a")
public class EntityA extends DataObject {
}
#Entity
#SequenceGenerator(initialValue = 1, name = "idgen", sequenceName = "entitybseq")
#Table(name = "entity_b")
public class EntityB extends DataObject {
}
I'm sorry I don't have the required environment to test it right now but I'll try it later.
We use this in the abstract superclass of all of our JPA entities:
#Id
#GeneratedValue(generator = "pooled")
#GenericGenerator(name = "pooled", strategy = "org.hibernate.id.enhanced.TableGenerator", parameters = {
#org.hibernate.annotations.Parameter(name = "value_column_name", value = "sequence_next_hi_value"),
#org.hibernate.annotations.Parameter(name = "prefer_entity_table_as_segment_value", value = "true"),
#org.hibernate.annotations.Parameter(name = "optimizer", value = "pooled-lo"),
#org.hibernate.annotations.Parameter(name = "increment_size", value = "100")})
private Long id;
It's a bit verbose, but it allows setting the prefer_entity_table_as_segment_value which means you don't need to repeat the id field or the generator annotations in the subclasses.
I was not quite happy about the need to declare the sequence name on each class individually. I have checked the source code and came up with this solution:
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
// ...
#Id
#GeneratedValue(generator = "sequenceIdGenerator")
#GenericGenerator(
name = "sequenceIdGenerator",
strategy = "sequence",
parameters = #Parameter(
name = SequenceStyleGenerator.CONFIG_PREFER_SEQUENCE_PER_ENTITY,
value = "true"))
#Column(updatable = false, nullable = false)
protected Long id;
IHMO there is better way to do this:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
It works in my app.
TABLE generation stretergy uses separate db sequence for each table but it is little expensive operation