I'm new with JPA, and want to create a Database with this relation :
|Participant|
|id : INT (PK) | id_event : INT (PK, FK) |
|Event|
|id : INT (PK) |
I'm totally lost and barely figure the syntax of the examples I found :/
But I understood I need to create an other class to contain the two pieces of the PK, which leads to another question : can this class be an inner-class (for optimisation purposes) ?
I hope I'm not asking too much but I really want to get it.
Your entities might be like this:
#Entity
public class Participant {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(fetch = FetchType.LAZY) // or any other relation
private List<Event> events;
// fields, constructors, getters, setters
}
#Entity
public class Event {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// fields, constructors, getters, setters
}
In this case JPA will create 3 tables with the following queries (SQL dialect will vary from DB to DB, in this case I used H2 database):
CREATE TABLE Event (
id bigint GENERATED BY DEFAULT AS IDENTITY,
PRIMARY KEY (id)
);
CREATE TABLE Participant (
id bigint GENERATED BY DEFAULT AS IDENTITY,
PRIMARY KEY (id)
);
CREATE TABLE Participant_Event (
Participant_id bigint NOT NULL,
events_id bigint NOT NULL
)
Participant_Event is automatically created join table to link participants and events.
Here is a good example of understanding JPA entity relations.
For a OneToMany relation you need the below entities and tables:
Participant
Event
The Event entity is simple:
#Entity
public class Event {
#Id
private Long id;
// fields, constructors, getters, setters
}
The entity Participant has to hold the composite key (aka two pieces of the PK), so, every Participant is only linked once with an Event.
#Entity
public class Participant {
#EmbeddedId
private EventParticipantPK id;
#OneToMany(fetch = FetchType.LAZY)
private List<Event> events;
// fields, constructors, getters, setters
}
The composite key is declared as an EmbeddedId.
The EventParticipantPK should be like:
#Embeddable
public class EventParticipantPK {
#Column (name = "PARTICIPANT_ID")
private Long participantId;
#Column (name = "EVENT_ID")
private Long eventId;
// fields, constructors, getters, setters
}
I hope this helps.
Related
I have three tables
CREATE TABLE "ingredient" (
"id" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
"ingredient" VARCHAR(50) NOT NULL
);
CREATE TABLE "pizza" (
"id" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
"pizza" VARCHAR(50) NOT NULL
);
CREATE TABLE "pizza_structure" (
"pizza_id" INT NOT NULL,
"ingredient_id" INT NOT NULL,
"amount" INT NOT NULL
);
how to join them, to get Pizzas structure as a Map
#Entity
#Table(name = "ingredient")
public class Ingredient{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Ingredient() {
}
}
#Entity
#Table(name = "pizza")
public class Pizza {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany ????
private Map<Ingredient, Integer> pizzaStructure;
public Pizza() {
}
public Pizza(String name, Map<Long, Integer> pizzaStructure) {
this.name = name;
this.pizzaStructure = pizzaStructure;
}
}
do I need to create #Embeddable class PizzaStructure, if yes when how to use it?
now I'm getting an error
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class:
how to join them, to get Pizzas structure as a Map
It seems to look like this:
#ElementCollection
#CollectionTable(name = "pizza_structure", joinColumns = {#JoinColumn(name = "pizza_id")})
#Column(name = "amount")
#MapKeyJoinColumn(name = "ingredient_id")
private Map<Ingredient, Integer> pizzaStructure;
do I need to create #Embeddable class PizzaStructure
No.
More info is here: Hibernate User Guide - Maps.
Note that table pizza_structure should have foreign keys to pizza and ingredient tables and also unique constrain of pizza_id and ingredient_id, like this (it's postgresql dialect):
create table pizza_structure
(
pizza_id ... constraint fk_structure_pizza references pizza,
ingredient_id ... constraint fk_structure_ingredient references ingredient,
amount ...,
constraint pizza_structure_pkey primary key (pizza_id, ingredient_id)
);
You have a manyToMany relationship between pizza and ingredient and an additional column in your relationship.
I found a similar question here: JPA 2.0 many-to-many with extra column
(I would comment, but i do not have enough reputation.)
It`s possible to create one map with hibernate #ManyToOne just like this:
public class IndicadorAtos {
#JsonIgnore
#Id
#Column(name="cod_ato_praticado")
private Integer codAtoPraticado;
#Column(name="descricao_ato")
private String ato;
#JoinColumn(name = "cod_ato", referencedColumnName = "cod_ato")
#ManyToOne
#Fetch(FetchMode.SUBSELECT)
private Atos atos;
}
But in some cases I dont have association or in my table IndicadorAtos have one code, that don`t existis in table Atos
this is my tables:
create table IndicadorAtos (
codAtoPraticado integer primary key,
ato varchar(250),
cod_ato integer
);
create table Atos(
cod_ato integer primary key.
name varchar(250)
)
I try to create this join:
Select t FROM IndicadorAtos t , Atos a where t.cod_ato = a.cod_ato, but I need to return all records from my IndicadorAtos, and with this select he only return all itens that have one item in Atos.
tks
It`s possible to create one map with hibernate #ManyToOne
Yes; it is called unidirectional relationship.
If I understood your question properly, you want to select all entries from IndicadorAtos with possibly associated entries from Atos. You can achieve this by using left join as follows:
SELECT t FROM IndicadorAtos t LEFT JOIN t.atos at
provided that you have an entity Atos defined like:
#Entity
public class Atos {
#Id #GeneratedValue
private int cod_ato;
private String name;
// getters and setters
}
I'am struggling to get Hibernate (with MySQL) to generate the primary key for a reference table from a "main table". My problem is that I got a big table with 25 mil rows and now I need to add multiple additional columns and because in the future where will be even more columns to add I choose the way to work with reference tables instead of adding the columns to the main table (the rebuild takes hours... :)
So there is a main table and a reference table. In my conception the primary key of the reference table should be generated from the primary key of the main table. I could first insert an entry into the main table, then select it and use its primary key for the insert into the reference table, but this seems not to be the best way to me. So I would like to use Hiibernate's generators, but I can't figure out how.
Here's the main table:
#Entity
#Table
public class Task {
#Id
#GeneratedValue
#Column()
private Integer id;
// ...
#OneToOne(mappedBy = "task_ref", orphanRemoval=true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Stuff stuff;
// ...
}
And the reference table:
#Entity
#Table
public class Stuff {
#Id
#Column(name = "stuff_id")
#GeneratedValue()
private Integer stuff_id;
// ...
#OneToOne(orphanRemoval=true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Task task_ref;
// ...
}
So.. how can I use a generator to create the primary key for the table Stuff from the primary key of the table Task ?
Any suggestions or other solutions are highly welcome!
Thanks!
This is how you should map your bidirectional OneToOne association with a shared primary key:
#Entity
#Table
public class Task {
#Id
#GeneratedValue
private Integer id;
// ...
#OneToOne(mappedBy = "task_ref", orphanRemoval=true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Stuff stuff;
// ...
}
#Entity
#Table
public class Stuff {
#Id
#Column(name = "stuff_id")
private Integer stuff_id;
// ...
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "stuff_id")
private Task task_ref;
// ...
}
Only the parent needs to cascade to the Child entity, not the other way around.
The Parent only has an "inverse" side of the association.
The shared primary key is both a primary key and a foreign key in the Child entity
The MapsId annotation allows you to share the primary key for both the #Id and the OneToOne association
You could use the Task object as your id, see EmbeddedId or IdClass for an example. In case Stuff represents a subclass of the Task entity in your domain model, you should model this entity as such, in which you will have to represent the Stuff entity as a Joined Subclass of the Task entity. In both cases the extra Id in the Stuff entity is not needed anymore.
i am trying on many to many relationship, Team member can work on multiple projects and a project can have multiple team member , the table structure is as follows,
create table TBL_PROJECT_ONE(
id integer primary key generated always as identity(start with 12,increment by 3),
name varchar(50)
)
create table TBL_TEAM_MEMBER_ONE(
id integer primary key generated always as identity(start with 7,increment by 5),
name varchar(50),
salary integer
)
create table EMP_PRJ_CADRE(
MEMBER_ID integer references TBL_TEAM_MEMBER_ONE,
PRJ_ID integer references TBL_PROJECT_ONE,
CADRE varchar(10),
constraint PK_001_EMP_TEAM primary key (MEMBER_ID,PRJ_ID)
)
Here i have created a new table just to store the relationship,
Now please follow the Employee entity,
#Entity
#Table(name="TBL_TEAM_MEMBER_ONE")
public class EmployeeEntityFour implements Serializable{
public EmployeeEntityFour(){}
public EmployeeEntityFour(String empName,Integer salary){
...
..
}
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="ID")
private Integer empId;
#Column(name="NAME")
private String empName;
#Column(name="SALARY")
private Integer empSal;
#ElementCollection(fetch= FetchType.LAZY)
#CollectionTable(name="EMP_PRJ_CADRE")
#MapKeyJoinColumn(name="PRJ_ID")
#Column(name="CADRE")
private Map<ProjectEntityOne,String> employeeCadre;
...
..
.
}
Please follow the mapping for Project Entity,
#Entity
#Table(name="TBL_PROJECT_ONE")
public class ProjectEntityOne implements Serializable{
public ProjectEntityOne(){}
public ProjectEntityOne(String name){
this.projectName = name;
}
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="ID")
private Integer projectId;
#Column(name="NAME")
private String projectName;
#ElementCollection(fetch= FetchType.LAZY)
#CollectionTable(name="EMP_PRJ_CADRE")
#MapKeyJoinColumn(name="MEMBER_ID")
#Column(name="CADRE")
private Map<EmployeeEntityFour,String> employeeCadre;
....
..
.
}
In main method testing the code written is as follows,
ProjectEntityOne proj = new ProjectEntityOne("Citi Grand Central");
Map<EmployeeEntityFour,String> cadreMap = new HashMap<EmployeeEntityFour,String>();
cadreMap.put(new EmployeeEntityFour("Murlinarayan Muthu",34000), "Senior Software Engineer");
cadreMap.put(new EmployeeEntityFour("Gopalkrishna Rajnathan",64000), "Software Engineer");
cadreMap.put(new EmployeeEntityFour("Premanna Swaminathan",94000), "Project Manager");
proj.setEmployeeCadre(cadreMap);
em.persist(proj);
but i am getting an error which is
ERROR: 'PROJECTENTITYONE_ID' is not a column in table or VTI 'APP.EMP_PRJ_CADRE'.
When in both the entities i have specified #MapKeyJoinColumn than too i am getting an error as improper column for the third table.
Where i am missing
It somehow worked, i had to do some changes in the code,
first, the edited code in Entity ProjectEntityOne is as follows,
#ElementCollection(fetch= FetchType.LAZY)
#CollectionTable(name="EMP_PRJ_CADRE",joinColumns=#JoinColumn(name="PRJ_ID"))
#MapKeyJoinColumn(name="MEMBER_ID")
#Column(name="CADRE")
private Map<EmployeeEntityFour,String> employeeCadre;
What i have done here is i added #JoinedColumn in #CollectionTable,
Second change i did in Entity EmployeeEntityFour, the change is I removed Map of PorjectEntityOne from it,
in test,
i can save Project with Employee mapping but here all the employees should be already saved one.
i.e. the key of map
Map<EmployeeEntityFour,String> employeeCadre;
should be already persisted
and than we can persist project entity.
On employeeCadre in EmployeeEntityFour you need a #JoinColumn(name="MEMBER_ID") and you would also need a #JoinColumn(name="PRJ_ID") in the ProjectEntityOne employeeCadre.
But, I would not model it this way. First of all you cannot have a bi-directional ElementCollection mapping, and ElementCollection can only be owned by one side. The best solution would be to define an Cadre entity mapping to EMP_PRJ_CADRE table and have a OneToMany to it from both sides, and have it have a ManyToOne to each.
Alternatively you may use a ManyToMany with a MapKeyColumn, but I think you would be better off having an entity.
I have a problem very similar to this: How do I join tables on non-primary key columns in secondary tables?
But I'm not sure if I can apply the same solution.
I have two tables like these:
CREATE TABLE CUSTOMER
(
CUSTOMER_ID INTEGER NOT NULL,
DETAIL_ID INTEGER NOT NULL,
PRIMARY KEY( CUSTOMER_ID ),
CONSTRAINT cust_fk FOREIGN KEY( DETAIL_ID ) REFERENCES DETAILS( DETAIL_ID )
)
CREATE TABLE DETAILS
(
DETAIL_ID INTEGER NOT NULL,
OTHER INTEGER NOT NULL,
PRIMARY KEY( DETAIL_ID )
)
I'd like to map these tables to a single class called Customer, so I have:
#Entity
#Table(name = "CUSTOMERS")
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID"))
public class Customer {
#Id
#GeneratedValue
#Column(name = "CUSTOMER_ID")
private Integer id;
#Column(table = "DETAILS", name = "OTHER")
private Integer notes;
// ...
}
but this works only if DETAIL_ID matches CUSTOMER_ID in the primary table.
So my question is: how can i use a foreign-key field in my primary table to join on the primary-key of the secondary table?
UPDATE
I tried to set:
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID", referencedColumnName="DETAIL_ID"))
but when I run the application I get this exception:
org.hibernate.MappingException: Unable to find column with logical name: DETAIL_ID in org.hibernate.mapping.Table(CUSTOMERS) and its related supertables and secondary tables
For anyone looking for an answer to this, using #SecondaryTable is not the way to join two tables with non-primary key columns, because Hibernate will try to assosiate the two tables by their primary keys by default; you have to use #OneToMany review http://viralpatel.net/blogs/hibernate-one-to-many-annotation-tutorial/ for a solution, here's a code snippet in case that url stops working:
Customer Class:
#Entity
#Table(name="CUSTOMERS")
public class Customer {
#Id
#GeneratedValue
#Column(name="CUSTOMER_ID")
private Integer id;
#ManyToOne
#JoinColumn(name="DETAIL_ID")
private Details details;
// Getter and Setter methods...
}
Details Class:
#Entity
#Table(name="DETAILS")
public class Details {
#Id
#GeneratedValue
#Column(name="DETAIL_ID")
private int detailId;
#Column(name="OTHER")
private String other;
#OneToMany(mappedBy="details")
private Set<Customer> customers;
// Getter and Setter methods...
}
This is easily accessible through hibernate with the following code:
Session session = HibernateUtil.getSessionFactory().openSession();
Query query = session.createQuery("select id, details.other from Customer");
I hope this helps anyone out there spending hours searching for a way to achieve this like I did.
You can use the referenceColumnName attribute of the #PrimaryKeyJoinColumn annotation to define the join column to the referenced table. In fact, by combining use of name/referencedColumnName you can join on arbitrary on both sides, with the constraint that if duplicates are found your ORM provider will throw an exception.