Am having two models named Style and StyleExp with thier respective PK classes and am using JPA for mapping.
The Style class contains four variables namely pid, Sname, Screen, StyleId. and the class StyleExp has the varibales named StyleId,EId, sno,exp.
In the class Style pid, Sname, Scrn are primary keys and in the class StyleExp StyleId,EId are the primary keys. Am having one to many relation between these two classes.
I have provided the mapping like the following in Style class,
#Entity(name="Style")
#Table(name="Style")
#IdClass(StylePk.class)
public class Style implements Serializable{
#Id
private String pid;
#Id
private String Sname;
private String styleId;
.....
#OneToMany(fetch=FetchType.Lazy, mappedBy="style")
protected List<StyleExp> styleExp;
}
In the class STyleExp I have provided the Mapping as follows,
#Entity(name="StyleExp")
#Table(name="StyleExp")
public class StyleExp implements Serializable{
#Id
private String styleId;
#Id
private String eId;
#ManyToOne(fetch=FetchType.Lazy)
#JoinColumns({
#JoinColumn(name="styleId",
referencedColumnName="styleId",insertable=false,updatable=false)
})
protected Style style;
}
But when Am running these code as getting the List of StyleExp from Style class as
Style style = styleDao.getStyle(pid, Sname, Scrn)
List<StyleExp> styleExpList = style.getStyleExp();
It throws the following error
causedBy : org.hibernate.AnnotationException: referencedColumnName(styleId) of StyleExp.style referencing model.Style not mapped to a single property
So please kindly let me know what mistake am doing? and one more doubt for me is non primary key and primary key OneToMany and ManyToOne mapping is possible in JPa?
Because of the non primary key mapped to primary key is the problem in the above situation?
Kindly help me.
Thanks in Advance.
I have tried by the following for OneToMany mapping between Style and StyleExp
#Entity(name="Style")
#Table(name="Style")
public class Style implements Serializable{
private String pid;
private String Sname;
#Id
private String styleId;
.....
#OneToMany(fetch=FetchType.Lazy, cascade=CascadeType.ALL)
#JoinColumns({
#JoinColumn(name="styleId", referencedColumnName="styleId" insertable=false, updatable=false)
})
protected List<StyleExp> styleExp;
}
In the class StyleExp I have provided the Mapping as follows,
#Entity(name="StyleExp")
#Table(name="StyleExp")
public class StyleExp implements Serializable{
#Id
private String styleId;
#Id
private String eId;
}
By using the above code I can map the above two tables
You should only use #Id for primary key in your parent class Style.
Other columns that you want to set it as unique, then doing as below:
// primary key
#Id
#Column(name = "pid", unique = true, nullable = false)
private String pid;
// unique key
#Column(name = "styleId", unique = true, nullable = false)
private String styleId;
Then in your StyleExp class, you can map to the unique column styleId in Style class as below
// you are referencing to unique column in parent table
#ManyToOne
#JoinColumn(name = "styleId", nullable = false, referencedColumnName = "styleId")
protected Style style;
The "referencedColumnName" is used when you want to map to a none primary column in parent table.
Anyway, no need to use multiple #Id in a table/model class.
Related
I'm working with Spring Data JPA and I'm trying to create 4 different entities that will have exactly the same fields but they will be stored in 4 different tables.
This is my key class
public class IndexId implements Serializable {
private int seqNo;
private String index;
// getters and setters
}
Then I have the base class:
#MappedSuperclass
public class BaseIndex {
#Id
#Column(name = "seq_no", nullable = false)
protected int seqNo;
#Id
#Column(name = "index", nullable = false)
protected String index;
#Column(name = "value", nullable = false)
protected String value;
//getters/setters
}
Then my entity that will store in the database:
#Entity
#IdClass(IndexId.class)
#Table(name = "bibliographic_single_index")
public class BibliographicSingleIndex extends BaseIndex implements Serializable { }
This is the error I get: Persistent entity 'BibliographicSingleIndex' should have primary key .
I also tried with the properties declared as private and the articles I see on this subject seem to do the same thing.
With these pieces of code is it possible to identify what I'm doing wrong?
I believe every entity needs a separate java class for the id class. You wouldn't have the problem with embedded ids I think.
I'm having a hard time trying to figure out how to properly do ORM on a certain database design.
My schema consists of three tables: a user table, a review table, and a vote table. Users can publish reviews for albums, and they can also assign a positive or negative rating to any review. Albums are provided from an external API, so their table is missing from the schema, but their IDs are referenced.
A user primary key simply consists of their username. A review primary key is composed of the reviewer's username, which is a foreign key, and the reviewed album ID. Finally, a vote primary key is composed of the voter's username, again a foreign key, and the voted review's primary key, consisting, as said earlier, of the reviewer's username and the reviewed album ID.
A user can publish a review for each individual album, and also can assign a vote for each individual review.
This is the ER model representing the schema:
To map the entities IDs, I'm using the #IdClass annotation, but I'm not sure I'm headed in the right direction. I also tried using the #EmbeddedId annotation, but the result is the same.
This is what my entities classes look like so far:
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 1;
#Id #Column(name = "username")
private String username;
#Column(unique = true, nullable = false)
private String email;
#Column(name = "password", nullable = false)
private String password;
#Temporal(TemporalType.TIMESTAMP) #Column(name="signUpDate", nullable = false)
private Date signUpDate;
// empty constructor, getters, setters, equals and hashCode implementations
}
#Entity #IdClass(ReviewId.class)
public class Review implements Serializable {
private static final long serialVersionUID = 1;
#Id #ManyToOne #JoinColumn(name = "reviewerUsername", referencedColumnName = "username")
private User reviewer;
#Id #Column(name = "reviewedAlbumId")
private Long reviewedAlbumId;
#Column(name = "content", nullable = false, length = 2500)
private String content;
#Column(name = "rating", nullable = false)
private Integer rating;
#Temporal(TemporalType.TIMESTAMP) #Column(name = "publicationDate", nullable = false)
private Date publicationDate;
// empty constructor, getters, setters, equals and hashCode implementations
}
#Entity #IdClass(VoteId.class)
public class Vote implements Serializable {
private static final long serialVersionUID = 1;
#Id #ManyToOne #JoinColumn(name = "voterUsername", referencedColumnName = "username")
private User voter;
#Id #ManyToOne #JoinColumns({
#JoinColumn(name = "reviewerUsername", referencedColumnName = "reviewerUsername"),
#JoinColumn(name = "reviewedAlbumId", referencedColumnName = "reviewedAlbumId")
})
private Review review;
#Column(name = "vote") // #todo add attribute nullable = false
private Boolean vote;
// empty constructor, getters, setters, equals and hashCode implementations
}
These are my ID classes:
public class ReviewId implements Serializable {
private static final long serialVersionUID = 1L;
private User reviewer;
private Long reviewedAlbumId;
// empty constructor, getters, setters, equals and hashCode implementations
}
public static class VoteId implements Serializable {
private static final long serialVersionUID = 1L;
private User voter;
private Review review;
// empty constructor, getters, setters, equals and hashCode implementations
}
And here is the content of the MySQL script used to generate the schema:
DROP SCHEMA IF EXISTS albumReviewsDatabase;
CREATE SCHEMA albumReviewsDatabase;
USE albumReviewsDatabase;
CREATE TABLE user (
username VARCHAR(20) PRIMARY KEY,
email VARCHAR(254) NOT NULL UNIQUE,
password CHAR(60) NOT NULL,
signUpDate TIMESTAMP NOT NULL DEFAULT now()
) ENGINE = INNODB;
CREATE TABLE review (
reviewerUsername VARCHAR(20) NOT NULL,
reviewedAlbumId BIGINT(20) NOT NULL,
content TEXT NOT NULL,
rating SMALLINT UNSIGNED NOT NULL,
publicationDate TIMESTAMP NOT NULL DEFAULT now(),
CHECK (rating >= 0 AND rating <= 10),
PRIMARY KEY (reviewerUsername, reviewedAlbumId),
FOREIGN KEY (reviewerUsername) REFERENCES user(username)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE = INNODB;
CREATE TABLE vote (
voterUsername VARCHAR(20) NOT NULL,
reviewerUsername VARCHAR(20) NOT NULL,
reviewedAlbumId BIGINT(20) NOT NULL,
vote BOOLEAN NOT NULL,
PRIMARY KEY (voterUsername, reviewerUsername, reviewedAlbumId),
FOREIGN KEY (voterUsername) REFERENCES user(username)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (reviewerUsername, reviewedAlbumId) REFERENCES review(reviewerUsername, reviewedAlbumId)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE = INNODB;
I'm currently using OpenJPA as the persistence provider on a TomEE webprofile instance, and the used JPA version is 2.0.
Clearly I am misunderstating something about JPA's ORM, because when I deploy my application containing those entities I get the following exception:
<openjpa-2.4.2-r422266:1777108 fatal user error> org.apache.openjpa.util.MetaDataException: The id class specified by type "class application.model.Review" does not match the primary key fields of the class. Make sure your identity class has the same primary keys as your persistent type, including pk field types. Mismatched property: "reviewer"
The exception is thrown because of the Review class mapping, and not the Vote class; however, I am sure that by solving the issue on the Review class, the same will reappear for Vote.
I'd prefer to get away with using the #IdClass annotation instead of #EmbeddedId, but whichever of the two I will end up using is not an issue.
These relationships are "derived identities"; so your ID classes should look like this (note the types of the foreign key fields differ from the types of their corresponding entity fields):
public class ReviewId implements Serializable {
private static final long serialVersionUID = 1L;
private String reviewer; // matches name of #Id attribute and type of User PK
private Long reviewedAlbumId;
// ...
}
public static class VoteId implements Serializable {
private static final long serialVersionUID = 1L;
private String voter; // matches name of #Id attribute and type of User PK
private ReviewId review; // matches name of #Id attribute and type of Review PK
// ...
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
Also, as a side note, #IdClass is a bit Old School while #EmbeddedId is cleaner, eliminating the code duplicated across the entity and its key.
I'm mapping a relationship that does not use the entity's primary key. Using "referencedColumnName" with a column different than the primary key causes hibernate to eagerly fetch the association, by issuing an extra select, even when it's tagged with FetchType.LAZY.
My goal is to make it behave like a regular mapping, meaning it wouldn't issue an extra query every time I need to query the main entity.
I have already tried using #LazyToOne(LazyToOneOption.NO_PROXY), which sorts out the problem, but it does not operate well with Jackson's (JSON parsing library) module "jackson-datatype-hibernate5", which skips hibernate lazy proxies when serializing the results.
Here is a scenario almost like the one I have that causes the problem:
Entities:
#Entity(name = "Book")
#Table(name = "book")
public class Book
implements Serializable {
#Id
#GeneratedValue
private Long id;
private String title;
private String author;
#NaturalId
private String isbn;
//Getters and setters omitted for brevity
}
#Entity(name = "Publication")
#Table(name = "publication")
public class Publication {
#Id
#GeneratedValue
private Long id;
private String publisher;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "isbn",
referencedColumnName = "isbn"
)
private Book book;
#Column(
name = "price_in_cents",
nullable = false
)
private Integer priceCents;
private String currency;
//Getters and setters omitted for brevity
}
Repository (Spring-Data, but you could try directly with the EntityManager):
#Repository
public interface PublicationRepository extends JpaReadRepository <Publication, Long>
{
#Query ("SELECT d FROM Publication d WHERE d.publisher = ?1 ")
Optional <Publication> findByPublisher (String isbn);
}
Thanks
The only way to achieve what you are looking for is by moving the annotatation #Id to the isbn property.
You can leave the #GeneratedValue on the autoincrement property.
Notes:
1 - Make sure that your equals/hc are following the OID(Object ID) on your domain case the "NaturalId" ISBN.
2 - It will be good to ensure if possible on DB level that your natural ID has unique contraint on it.
I want to have hibernate generate some tables with foreign keys and so on. Ill give you an example of the query i want hibernate to generate:
create table RealtimeCost(id INTEGER not null primary key Autoincrement,
mnemonic varchar(50)not null references Exchange(mnemonic),
sid int not null references License(sid),
price numeric(10,2) not null)
so this query should be generated by hibernate via Annotations. The corresponding class to this is:
#Entity
#Table
public class RealtimeCost {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#MapsId("mnemonic")
#JoinColumn(referencedColumnName="sid")
private String mnemonic;
#MapsId("sid")
#JoinColumn(referencedColumnName="sid")
private Integer sid;
#Column
private Double price;
Example for what the mnemonic in RealtimeCost should be mapped to (each mnemonic in RealtimeCost has exactly 1 value in Exchange):
#Entity
#Table
public class Exchange {
#Id
#Column(name="mnemonic")
private String exchange;
#Column
private String description;
As you can see I've tried a bit with the help of the docs, but I was not able to have the foreign keys be generated by hibernate. It would be really kind, if anyone could tell me the needed annotations and values for this class, so i can do it myself for the other classes as well. Also please tell me if i need to change anything in the Exchange class for the mapping to work. Thanks in advance
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "accommodation_type", unique = true, nullable = false)
private AccommodationType accommodationType;
#ManyToOne()creates a relationship according to #JoinColumn()
name in #JoinColumn() is the table name that you want to make a connection.
Then when you create a class that is going to be connected to main class, you first need to give it a table name below #Entity e.g #Table(name="accommodation_types")
Then you create your variable.
//bi-directional many-to-one association to Accommodation
#OneToMany(mappedBy="accommodationType", fetch=FetchType.EAGER)
private List<Accommodation> accommodations;
value of mappedByis the variable name in main class.
I'm not an expert but we let hibernate do all the work with the javax.persistence annotations for joining entities.
#javax.persistence.ManyToOne( fetch = javax.persistence.FetchType.EAGER, optional = true )
#javax.persistence.JoinColumn( name = "VIEWTYPE_ID", nullable = true, unique = false, insertable = true, updatable = true )
private com.company.other.subproject.ViewType viewType;
Maybe this is what you need. Since this let's hibernate care about the tables that have to be created or not and the foreignKeys get created automatically with the dialect of the database you communicate with.
You should set up the association in one entity and use the mappedBy in the other. You don't need #MapsId because you are not using embedded entities (read the docs). Take a look at the #OneToMany and #ManyToOne relationships:
#Entity
#Table
public class RealtimeCost {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany
#JoinColumn(name="mnemonic")
private Exchange exchange;
...
}
#Entity
#Table
public class Exchange {
#Id
#Column(name="mnemonic")
private String mnemonic;
#Column
private String description;
#ManyToOne(mappedBy="exchange")
private RealtimeCost realtimeCost;
...
}
Every answer posted here got an upvote from me, because everyone was kinda right, but it was not 100% what i was searching for, yet it helped me solving my problem by myself. For the example i posted, the solution i was seeking is as follows (i also added not nullable):
#Entity
#Table
public class RealtimeCost {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "mnemonic",nullable=false)
private Exchange exchange;
#ManyToOne
#JoinColumn(name = "sid",nullable=false)
private License license;
#Column(nullable=false)
private Double price;
these are the annotations i was seeking for RealtimeCost class. I did not need any special annotations in Exchange class. #Nico answer was closest to what i need, therefore his answer will be accepted
I got these 2 entities:
#javax.persistence.Entity
public class Book {
#javax.persistence.EmbeddedId
private BookPK id;
private String title;
#javax.persistence.ManyToOne(fetch = javax.persistence.FetchType.LAZY)
#javax.persistence.JoinColumns({
#javax.persistence.JoinColumn(name = "LNGCOD", referencedColumnName = "LNGCOD"),
#javax.persistence.JoinColumn(name = "LIBCOD", referencedColumnName = "LIBCOD") })
private Language language;
}
#javax.persistence.Entity
public class Language {
#javax.persistence.EmbeddedId
private LanguagePK id;
private String name;
}
with composed PK's:
#Embeddable
public class BookPK implements Serializable {
private Integer bookcod;
private Integer libcod;
}
#Embeddable
public class LanguagePK implements Serializable {
private Integer lngcod;
private Integer libcod;
}
If I try to create a new Book and persist it, I get an exception telling me libcod is found twice in the insert statement ("Column 'libcod' specified twice"). But I can't use "insertable = false" when defining the JoinColumn ("Mixing insertable and non insertable columns in a property is not allowed").
Is there any way to define these objects + relationship so the columns are managed automatically by Hibernate ? (I am especially thinking of libcod).
Thank you.
Create a third property "Integer libcod;" on the Book. Have that property manage the db state of libcod. Use insertable=false,updatable=false for both properties in the join to Language. in your "setLanguage" set the private libcod = language.libcod. don't expose a getter/setter for the private libcod.
Are any of the values generated at insert time? This could complicate things further, I suppose.