Same ID from sequence for 2 tables - JPA - java

I have 2 entities that use the same sequence as the primary key, how do I map?
Example:
#Entity
#Table("employeT")
public class Employe(){
#SequenceGenerator(name = "generator_id", sequenceName = "seq_id")
#GeneratedValue(generator = "generator_id")
#colunm(name = "id")
private Integer id;
#colunm(name = "nameEmp")
private String name;
#JoinColumn(name = "id")
private Computer computer;
}
#Entity
#Table("computerT")
public class Computer(){
#SequenceGenerator(name = "generator_id", sequenceName = "seq_id")
#GeneratedValue(generator = "generator_id")
#colunm(name = "id")
private Integer id;
#colunm(name="name_computer")
private String nameComputer;
}
I need save employe and computer with same id, generated by Employe save.

There are three things to do with your code to work the way to want to.
Add #OneToOne annotation to indicate that Employee and Computer are in relation.
Delete information about #SequenceGenerator from your Computer entity and add #Id annotation
Add #MapsId annotation. [More info]
So it would look something like this :
#Entity
#Table("employeT")
public class Employe(){
#Id
private Integer id;
#Colunm(name = "nameEmp")
private String name;
#OneToOne
#JoinColumn(name = "computer_id")
#MapsId
private Computer computer;
}
Why?
#OneToOne annotation indicates relation between entities.
#SequenceGenerator is redudant since we "copy" id from Computer entity.
#Id annotation is mandatory to indicate that this field is our primary key.
Last but not least, #MapsId annotation do the magic, where it 'borrows' id from relation.
More info in the link I attached earlier.

Related

JPA not saving foreign key in one-to-many relation

I have 2 tables with one-to-many relation on the owner class (Person) and many-to-one on the child class (Email)
My problem is that in the child class' foreign key is (person_id) is always null when I want to save my Person object. I tried different things using other questions' answers, but no luck.
I would like to solve this in an annotation approach, if it is possible.
Person Class:
#Entity
#Table(name="PERSON")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PERSON")
#SequenceGenerator(name="SEQ_PERSON", sequenceName="SEQ_PERSON", allocationSize=1)
#Column(name = "person_id")
private Long personId;
#OneToMany(mappedBy="person", cascade=CascadeType.ALL)
private List<Email> email;
// getters and setters
}
Email class:
#Entity
#Table(name="EMAIL")
public class Email{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_EMAIL")
#SequenceGenerator(name="SEQ_EMAIL", sequenceName="SEQ_EMAIL", allocationSize=1)
#Column(name = "email_id")
private Long emailId;
#ManyToOne
#JoinColumn(name="person_id", referencedColumnName="person_id", insertable = true)
private Person person;
// getters and setters
}
I get no exception / errors when I use this.
When I change the JoinColumn to #JoinColumn(name="person_id", referencedColumnName="person_id", nullable = false, updatable = false, insertable = true) then I get this error: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.test.Email.person
I tried to change the Person's email setter like this, nothing changed:
public synchronized void setEmail(List<Email> email) {
this.email=email;
for(Email em: email) {
em.setPerson(this);
}
}
source
I have a Person object, with 2 emails (as a test object to save, every column is filled, except the FK in Email table), do I have to set the FK everytime manually? (it doesn't look good, if I have multiple one-to-many variables)
Edit: I tried this Which is working, but my problem with that if I have a very deep data structure with a lot of One-To-Many relations, I have to implement this to every variable and then save.. So, is there a better solution with pure annotations / getters-setters ?

How to setup JPA Entity classes?

I am struggling with how to setup my JPA entity classes and which annotations should go where
I have the following tables:
Table Customer {
id: primary key,
name
}
Table CustomerDimension {
id: primary key, foreign key(Customer.id),
detail
}
Currently I have the following entity classes:
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#OneToOne
private CustomerDimension customerDimension;
}
public class CustomerDimension {
// ? what is meant to go here?
private long id;
#Column(name = "detail")
private String detail;
}
What annotation is meant to go on CustomerDimension.id to allow me to insert a new Customer that has a new CustomerDimension?
Should CustomerDimension also have a reference back to Customer?
Table Customer {
id: primary key,
name
}
Table CustomerDimension {
id: primary key,
foreign key(Customer.id),
detail
}
CustomerDimension is the owning side. so, the #OneToOne mapping should be like
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
}
public class CustomerDimension {
#Id
private long id;
#Column(name = "detail")
private String detail;
#OneToOne
private Customer customer;
}
You have the following problems :
Customer and CustomerDimension need the annotation #Entity.
In your DDL, the table CustomerDimension has a foreign key on Customer. Hence, the #OneToOne relationship should be declared on CustomerDimension's side.
Still in the DDL, your foreign key does not have an explicit name. I will assume it is customer_id and use it to declare the #JoinColumn (see below)
#Column annotations are required only if you need the column to have a name which is different from the attribute's name (but you can keep them for clarity).
Here is how I would map it.
#Entity
#Table(name = "Customer") //Optional
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name") //Optional
private String name;
}
And for CustomerDimension :
#Entity
#Table(name = "CustomerDimension") //Optional
public class CustomerDimension {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "detail") //Optional
private String detail;
#OneToOne
#JoinColumn(name = "customer_id") //NOT optional
private Customer customer
}
EDIT (answer to your comment) :
If you really want your FK to be the primary key, you can do it like this :
#Entity
#Table(name = "CustomerDimension") //Optional
public class CustomerDimension {
#Column(name = "detail") //Optional
private String detail;
#Id
#OneToOne
#JoinColumn(name = "id") //NOT optional
private Customer customer
}
I still wonder why you do not put all information in the same table. It would save you a SQL join.
What you have here is a OneToMany biidirectional relationship with a foreign key instead of a join table. A join table seems to be preferred by vendors, but it's OK.
So, you have a list (or set) of CustomerDimensions in Customer, but with the mappedBy value set.
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy="customer")
List<CustomerDimensions> dimensions;
}
and
public class CustomerDimension {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "detail")
private String detail;
#ManyToOne
Customer customer;
}
It's natural that Customers have a set of dimensions. By having a bidirectional mapping, if you have a dimension, then you can look up the customer easy (just reference the customer field)
EDIT: Since the CustomerDimension table has a Customer id reference, you can select many CustomerDimensions for one Customer, hence a OneToMany relationship. In order to set the CustomerDimension.customer_id field, simply put a CustomerDimension in the Customers list of dimensions.

How to generate ID for entity from the existing ORACLE sequence?

I have a Table (for a long time ago), call it TABLE_A, and I have an entity class for this Table:
#Entity
#Table(name = "TABLE_A")
public class TableA implements Serializable {
#Id
#Basic(optional = false)
#Column(name = "ID")
//what else should I write here, to get the value from the existing sequence (seq_table_a_id) from database?
private Long id;
#Basic(optional = false)
#Column(name = "VALID_TO_DT")
private String name;
getters/setters...
}
I had created a sequence for this table in ORACLE a long time ago, and I want to give values for the new item's ID from this sequence. How should I write this code in java entity with annotations? If you could write an example for my code, that would be helpful!
And should I write anything else maybe in the persistance.xml?
The name of the existing sequence is: seq_table_a_id
You should check the annotation #GeneratedValue and #SequenceGenerator
#Id
#GeneratedValue(generator="seqGen")
#SequenceGenerator(name="seqGen",sequenceName="seq_table_a_id", allocationSize=1)
private Long id;
Check this link

JPA modelling, one-to-one relation?

I am new to JPA and stuggles with defining the relations between my classes. I have a class called Player and a class called Game. A game holds references to two Player instances. The question is, how should this be modelled?
This is my current code:
#Entity
#Table(name = "t_player")
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class Player {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Basic
#Column(name = "name")
private String name;
#Basic
#Column(name = "uuid")
private final String uuid = UUID.randomUUID().toString();
I think this is ok, but my problem is in the Game class:
#Entity
#Table(name = "t_game")
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class Game {
public Game() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Basic
#Column(name = "uuid")
private final String uuid = UUID.randomUUID().toString();
#OneToOne
#PrimaryKeyJoinColumn
#JoinColumn(name = "id")
private Player player_1;
#OneToOne
#PrimaryKeyJoinColumn
#JoinColumn(name = "player_2")
private Player player_2;
public Game(Player player_1, Player player_2) {
this.player_1 = player_1;
this.player_2 = player_2;
}
}
This is not working, my table t_game only has two field; id and uuid. Where is my problem?
Remove the PrimaryKeyJoinColumn annotation, as I don't think it is what you meant to use, as it conflicts with the joincolumn definition. Use the joincolumn annotation instead to define the foreign key field name and the field it references if necessary.

Hibernate sequence on oracle, #GeneratedValue(strategy = GenerationType.AUTO)

I'm usign #GeneratedValue(strategy = GenerationType.AUTO) to generate the ID on my entity.
I don't now how it works, but on my child table, generates ID values, that follow the parent sequence.
//parent table
#Entity
#Table (name = "parent")
public class Parent {
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
#Column (name = "id")
private long id;
#OneToMany (cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinColumn (name = "parentId")
#ForeignKey (name = "FKparent")
private List<child> child;
}
//child table
#Entity
#Table (name = "child")
public class Child {
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
#Column (name = "id")
private long id;
}
The inserted ID values on parent, updates the sequence.
The inserted ID values on child, updates the sequence.
On the next insert of parent, the sequence... uses values updated by child insertions...
This Annotations, aren't creating two sequences, only one. Is this correct/expected?
I inserted my entities with my DAO service only using entityManager.persist(parent);
These Annotations are no creating two sequences, only one. Is this correct/expected?
That's the expected behavior. When using #GeneratedValue(strategy = GenerationType.AUTO), the JPA provider will pick an appropriate strategy for the particular database. In the case of Oracle, this will be SEQUENCE and, since you did not specify anything, Hibernate will use a single global sequence called hibernate_sequence.
Is this correct? Well, I don't know, it depends on your needs. Just in case, the default maximum value for an Oracle sequence is 1E+27, or 1,000,000,000,000,000,000,000,000,000. That's enough for many.
Now, it is possible to use GenerationType.AUTO and still control the name of the sequence when the database uses sequences:
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="my_entity_seq_gen")
#SequenceGenerator(name="my_entity_seq_gen", sequenceName="MY_ENTITY_SEQ")
private Long id;
Yes this is correct and expected.
You can create individual sequences for each table, but IMHO that is just extra code with no actual benefit.
#Entity
#Table(name = "table_seq")
#SequenceGenerator(name= "NAME_SEQUENCE", sequenceName = "SEQ_ID", initialValue=1, allocationSize = 1)
public class SeqEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="NAME_SEQUENCE")
private Long id;
}

Categories