I have a situation with repeatable class fields which I want to mark as #embeddable, however the question is - does JPA allow re-utilizing a class multiple times as embeddable in other different classes?
E.g. my embeddable class looks as follows:
#Embeddable
#Data
public class Audit{
private String name;
private Audit auditor;
private LocalDateTime creationDate;
}
Is it possible to embed the Audit into multiple different classes as for ex.:
#Entity
#Table(name = "BANK")
public class Bank{
#Id
private Long id;
#Column(name = "BANK_NAME")
private String bankName;
#Embedded
private Audit audit;
}
AND
#Entity
#Table(name = "CORPORATION")
public class Corporation{
#Id
private Long id;
#Column(name = "CORPORATION_NAME")
private String corporationName;
#Embedded
private Audit audit;
}
Historically Hibernate called these components. JPA calls them embeddables. Either way, the concept is the same: a composition of values.
Most often, embeddable types are used to group multiple basic type mappings and reuse them across several entities.
Java Code Example:
#Data
#Entity(name = "Book")
public class Book {
#Id
#GeneratedValue
private Long id;
private String title;
private String author;
private Publisher publisher;
}
#Data
#Embeddable
public static class Publisher {
#Column(name = "publisher_name")
private String name;
#Column(name = "publisher_country")
private String country;
}
And this is SQL to show how your table should look like:
create table Book (
id bigint not null,
author varchar(255),
publisher_country varchar(255),
publisher_name varchar(255),
title varchar(255),
primary key (id)
)
More details can be found in the documentation :)
Related
Interestingly, I can't find any solution for a seemingly common scenario! So I'm asking here to learn from experienced professionals in Spring Data JPA. I'll consider using Lombok to make the sample codes more concise.
Consider a simple IMDB example web application. I've defined two simple entities as below:
#Data
#Entity
public class Movie {
#Id
#GeneratedValue
private long id;
private String title;
private int year;
private int rating;
}
#Data
#Entity
public class Actor {
#Id
#GeneratedValue
private long id;
private String firstName;
private String lastName;
private Date birthday;
private String gender;
}
Now we need a join-table to link these two entities; but this is not just a simple join-table. Other than the actor and movie columns, this table has some additional attributes. We didn't want to waste storage by adding an ID column here, instead we used a composite-key consisting of actor and movie:
#Data
#Embeddable
public class MovieActorId implements Serializable {
private Actor actor;
private Movie movie;
}
#Data
#Entity
public class MovieActor {
#EmbeddedId
private MovieActorId id;
private int salary;
private String characterName;
}
There are two Many-to-One relations here: MovieActor >-- Actor and MovieActor >-- Movie.
Now my main question is: "Assuming the above design, how should I define the #ManyToOne relationships in this design?"
NOTE: I believe if we add an additional ID column to the MovieActor join-table instead of the composite/embedded MovieActorId, the JPA code will become fairly straight-forward. But suppose we have some sort of limitation, and we need to stick to this design as much as possible.
You need to use #MapsId which provides the mapping for an EmbeddedId primary key in #ManyToOne relation
#Data
#Embeddable
public class MovieActorId implements Serializable {
private long actorId;
private long movieId;
// constructor, setter, etc
}
#Data
#Entity
public class MovieActor {
#EmbeddedId
private MovieActorId id;
#ManyToOne(cascade = CascadeType.ALL)
#MapsId("actorId")
private Actor actor;
#ManyToOne(cascade = CascadeType.ALL)
#MapsId("movieId")
private Movie movie;
...
}
I work with mySQL, hibernate and Spring Datas and I want to link a table with more than one entity.
The table looks like this but more complex (with many-to-one relations...) :
CREATE TABLE FOOBAR (
id BIGINTEGER NOT NULL AUTO_INCREMENT,
type ENUM('FOO', 'BAR'),
name VARCHAR(30),
foo_style ENUM('ONE', 'TWO')),
PRIMARY KEY(id);
The entities that I want to create. I want a generic table that cans recover all the entries, and two tables that cans recover only the rows corresponding to the correct enum :
#Table
public class FooBar {
#Id
#Column
private String id;
#Column
#Enumerated(EnumType.STRING)
private Type type;
#Column
private String name;
#Column
#Enumerated(EnumType.STRING)
private FooStyle fooStyle;
}
#Table //On type foo
public class Foo {
#Id
#Column
private String id;
#Column
private String name;
#Column
#Enumerated(EnumType.STRING)
private FooStyle fooStyle;
}
#Table //On type bar
public class Bar {
#Id
#Column
private String id;
#Column
private String name;
}
Thanks in advance !
I have been scratching my head over this for weeks now and I have no idea how to work my way around it.
I have two tables:
Author (author_code, author_number, author_name) author_code and author_number are primary keys
Title (author_code, title_code, sequence, title_desc) title_code and sequence are primary keys
Code:
#Entity
#IdClass(AuthorPK.class)
#Table(name="Author")
public class Author implements Serializable {
#Id
#Column(name="author_code")
private long authorCode;
#Id
#Column(name="author_number")
private String authorNumber;
#Column(name="author_name")
private String authorName;
//bi-directional many-to-one association to Title
#OneToMany(mappedBy="author")
private List<Title> titles;
//getter setters
}
public class AuthorPK implements Serializable {
private long authorCode;
private String authorNumber;
//getter equals() hashCode()
}
#Entity
#IdClass(TitlePK.class)
#Table(name="Title")
public class Title implements Serializable {
#Id
#Column(name="title_code")
private long titleCode;
#Id
#Column(name="sequence")
private long sequence;
#Column(name="title_desc")
private String titleDesc;
//bi-directional many-to-one association to Author
#ManyToOne
#JoinColumns({
#JoinColumn(name="author_code", referencedColumnName="author_code"),
#JoinColumn(name="author_number", referencedColumnName="author_number")
})
private Author author;
//getters setters
}
public class TitlePK implements Serializable {
private long titleCode;
private long sequence;
//getter equals() hashcode()
}
I need to link the two entities via author_code but JPA requires me to include both IDs in the #JoinColumn... it's throwing errors on my application since the source table doesn't have the other column. Is there another way for me to join these entities?
I'm trying to retrieve a list of entities from a table with two primary keys which are ids to a foreign key each.
MySQL tables:
Paintings table:
id - int, PK, Auto increment
name - varchar(45)
Pictures table:
id - int, PK, Auto increment
name - varchar(45)
location - varchar(45)
painting_to_picture_link table:
painting_id - int, FK to id in painting
picture_id - int, FK to id in painting
I've set primary key (painting_id, picture_id)
and set them to their foreign keys also as written above.
In Java:
Painting.java
#Entity
#Table(name = "paintings")
public class Painting {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
...
}
Picture.java
#Entity
#Table(name = "pictures")
public class Pictures {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "location")
private String location;
...
}
PaintingPictureLink.class
public class PaintingPictureLink implements Serializable {
#<SOME ANNOTATION HERE>
private Painting painting;
#<SOME ANNOTATION HERE>
private Picture picture;
...
}
I've seen many examples, but didn't work for me.
I've tried putting #Id annotations, #EmbeddedId, etc... non worked.
The errors I get are that table isn't mapped, could not determine type for the models, missing #Id annotation... :|
Would appreciate help with querying this table and getting a list of PaintingPictureLink.
Some of the examples I've followed:
https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Web_Server/1.0/html/Hibernate_Annotations_Reference_Guide/ch03s02s06.html
Using an Entity (and their Primary Key) as another Entity's Id
Thanks,
Guy
Derp
Found the solution.
I created a primary key class:
#Embeddable
public class PicturePaintingPK implements Serializable {
#ManyToOne
private Painting painting;
#ManyToOne
private Picture picture;
public PicturePaintingPK() {}
// getters and setters //
}
In the PicturePaintingLink class:
#Entity
#Table(name = "painting_to_picture_link")
public class PaintingPictureLink implements Serializable {
#Id
private PicturePaintingPK primaryKey = new PicturePaintingPK()
...
//constructor//
...
public TTPK getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(TTPK primaryKey) {
this.primaryKey = primaryKey;
}
// ... all the other getters and setters needed .... //
}
The source for this solution was from:
Example from Hibernate forum
Guy
I have a following problem that I need to solve.
The core issues is that I want to add additional column into JoinTable for ManyToMany relation in JPA. In my case I have following entities.
The Topic is a simple entity which has many RemoteDocument's (one RemoteDocument may be refered by many Topic's, hence it should be ManyToMany relation). Also RemoteDocument entity is read only because it may be read only from Oracle Materialized View moreover any altering of this Materialized View is forbidden. So I want to store order of RemoteDocuments related to some Topic. In fact I can do something like that with additional entity:
#Entity
public class Topic {
#Id
private Long id;
#Basic
private String name;
#OneToMany
private Set<TopicToRemoteDocument> association;
}
#Entity
public class RemoteDocument {
#Id
private Long id;
#Basic
private String description;
}
#Entity
public class TopicToRemoteDocument {
#OneToOne
private Topic topic;
#OneToOne
private RemoteDocument remoteDocument;
#Basic
private Integer order;
}
In this case additional entity TopicToRemoteDocument helps me to replace ManyToMany association with OneToMany and add extra field order.
But I want to have ManyToMany relation but with configured additional column in join table
Use list instead of set, together with the #OrderColumn annotation and JPA will automatically take care of the order:
#MappedSuperclass
public class BaseEntity{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
public Long getId(){
return id;
}
public void setId(final Long id){
this.id = id;
}
}
#Entity
public class Topic extends BaseEntity{
#ManyToMany(mappedBy = "topics")
#OrderColumn
private List<Document> documents = new ArrayList<Document>();
public List<Document> getDocuments(){
return documents;
}
public void setDocuments(final List<Document> documents){
this.documents = documents;
}
}
#Entity
public class Document extends BaseEntity{
#ManyToMany
#OrderColumn
private List<Topic> topics = new ArrayList<Topic>();
public List<Topic> getTopics(){
return topics;
}
public void setTopics(final List<Topic> topics){
this.topics = topics;
}
}
Generated DDL (using hibernate and HSQL):
create table Document (
id bigint generated by default as identity (start with 1),
primary key (id)
);
create table Document_Topic (
documents_id bigint not null,
topics_id bigint not null,
topics_ORDER integer not null,
documents_ORDER integer not null,
primary key (documents_id, topics_ORDER)
);
create table Topic (
id bigint generated by default as identity (start with 1),
primary key (id)
);
alter table Document_Topic
add constraint FK343B5D0B481100B2
foreign key (documents_id)
references Document;
alter table Document_Topic
add constraint FK343B5D0B558627D0
foreign key (topics_id)
references Topic;
I would try to avoid using a List unless you allow duplicates.
There is a #OrderColumn annotation that automatically does this. Have you tried it?
#Entity
public class Topic {
#Id
private Long id;
#Basic
private String name;
#OneToMany
#OrderColumn
private Set<TopicToRemoteDocument> association;
}
One technique that is useful when creating the many-to-many mapping class entity is to attribute the id's in the class along with #ManyToOne designation which makes this class act as the composite key class:
#Entity
#Table(name = "market_vendor")
public class MarketVendor implements Serializable
{
#Id
#ManyToOne
#JoinColumn(name = "market_id")
private Market market;
#Id
#ManyToOne
#JoinColumn(name = "vendor_id")
private Vendor vendor;
#Basic
#Column(name="active")
private boolean active;
public MarketVendor(Market market, Vendor vendor, boolean active)
{
this.market = market;
this.vendor = vendor;
this.active = active;
}
}
This allows you to have the composite primary key defined within the same class without having to have a separate primary key class. You also need to make the class serializable.