I have the following situation:
I´m trying to build an application which is multi-tenant with the same tenants in one database
with the same tables. As by know Hibernate is not supporting this variant before 5.0 as I found.
I`m trying to solve this by adding a brandId field to every table.
As I build Many To Many relationships I also added this brandId to the ManyToMany Join Table and here (dont know if I can do this, mysql is not complaining) I made a foreign key to both tables while both include the brandid
So now for example I have a table Text(ID,name,brandId) and a Tag(ID,name,brandId) and a join table (text_id,tag_id,brand_id) where the foreign keys are
CONSTRAINT FK_TAG_TEXTS_TAG FOREIGN KEY (TAG_ID,BRAND_ID) REFERENCES TAG (ID,brand),
CONSTRAINT FK_TAG_TEXTS_TEXT FOREIGN KEY (TEXT_ID,BRAND_ID) REFERENCES TEXT (ID,brand)
As you can see Brand ID is used twice.
Then I generated my classes with Hibernate Tools, which created a Composite Primary Key Class as it should and the association in the Tag Class.
#ManyToMany(fetch = FetchType.EAGER,cascade = {CascadeType.PERSIST,CascadeType.MERGE })
#JoinTable(name = "tag_texts", , joinColumns = {
#JoinColumn(name = "TAG_ID", nullable = false, insertable = false, updatable = false),
#JoinColumn(name = "BRAND_ID", nullable = false, insertable = false, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "TEXT_ID", insertable = false, nullable = false, updatable = false),#JoinColumn( name = "BRAND_ID", insertable = false, nullable = false, updatable = false) })
public List<Text> getTexts() {
return this.texts;
}
The problem is now that I get the following exception:
org.hibernate.MappingException: Repeated column in mapping for collection: de.company.domain.Tag.texts column: brand_id
I looked into the Hibernate code in the Collection class which raises the exception.
Here a method 'checkColumnDupliation' is called which uses a Set and inserts the name,
what means that a second time inserting "BRAND_ID" as column leads to this behaviour.
As I found the most common solution for the Repeated column error is by inserting 'insertable = false and updateable = false' when using the same column in several references. This is described here:
Hibernate: Where do insertable = false, updatable = false belong in composite primary key constellations involving foreign keys?
But this seems to be not the same problem as mine.
So my question is: Is there a possibility to fix this with the JPA Annotations and use the Brand ID in both joinColumns and inverseJoinColumns?
The problem is that you want a JoinTable between 3 entities: Text, Tag and Brand.
Probably you will have to use an IdClass, something like :
public class AssociationId implements Serializable {
private long textId;
private long tagId;
private long brandId;
hash and equals function
...
}
Id Class Entity:
#Entity
#Table(name="tag_text_brand")
#IdClass(AssociationId.class)
public class TagTextBrandAssociation {
#Id
private long tagId;
#Id
private long textId;
#Id
private long textId;
#ManyToOne
#PrimaryKeyJoinColumn(name="TAG_ID", referencedColumnName="ID")
private Tag tag;
#ManyToOne
#PrimaryKeyJoinColumn(name="TEXT_ID", referencedColumnName="ID")
private Text text;
#ManyToOne
#PrimaryKeyJoinColumn(name="BRAND_ID", referencedColumnName="ID")
private Brand brand;
...
}
You can use this in your 3 entities like this:
#Entity
public class Text {
#Id
private long id;
...
#OneToMany(mappedBy="text")
private List<TagTextBrandAssociation> tagsAndBrands;
...
}
See here for more information.
Related
I have an existing database schema and I try to make one to many relationship in JPA when PK is a composite of multiple fields and just one of them is FK in the other entity:
DemandId: PK class that consist of two fields
#Embeddable
public class DemandId implements Serializable {
#Column(name = "\"ORDER\"", nullable = false)
private String order;
#Column(name = "SNRP", nullable = false)
private String number;
}
DemandEntity: The entity itself
#Entity
#Table(name = "DEMAND")
public class DemandEntity implements Serializable {
#EmbeddedId
private DemandId id;
#OneToMany(fetch = EAGER, cascade = ALL, mappedBy = "demand")
private Set<PartEntity> parts = new HashSet<>();
}
PartEntity:
#Entity
#Table(name = "PART")
public class PartEntity implements Serializable {
#Column(name = "SNRP")
private String number;
#ManyToOne
#JoinColumn(name = "SNRP", referencedColumnName = "SNRP", insertable = false, updatable = false)
private DemandEntity demand;
}
This approach leads to an exception:
Exception Description: The #JoinColumns on the annotated element
[field demand] from the entity class [class PartEntity] is incomplete.
When the source entity class uses a composite primary key, a
#JoinColumn must be specified for each join column using the
#JoinColumns. Both the name and the referencedColumnName elements must
be specified in each such #JoinColumn.
Unfortunatelly I cannot add another join column
#JoinColumn(name = "\"ORDER\"", referencedColumnName = "\"ORDER\"", insertable = false, updatable = false)
Because the PART table doesn't contain the ORDER field and the structure of the database cannot be changed.
Is there a way to perform such mapping?
Regards
If you have composite primary keys and you want to have one to many
mapping, I would suggest rather than keeping those keys as composite
primary keys, make them composite unique keys.
And make a auto-generated sequence as a primary key.
It is better and more convenient. By the way its my personnel opinion.
I even don't know, if that is possible or not which you are trying to do.
I have an entity with a composite primary key consisting of two fields, one of which is also part of a composite foreign key.
Background: I have entities Person,Area, and Session.
Person has many-to-many relationships with Area and Session, using join entities called PersonArea and PersonSession.
So, I have PersonSession, with primary key of (personId, sessionId).
PersonId and SessionId are themselves foreign keys to Person and Session.
PersonSession also has a field areaId.
I want (personId, areaId) to be a composite foreign key to PersonArea.
My code for PersonSession:
#Entity
#Table(name="person_session")
#IdClass(PersonSession.ID.class)
public class PersonSession {
#Id
private int personId ;
#Id
private int sessionId;
#ManyToOne
#JoinColumn(name = "personId", updatable = false, insertable = false, referencedColumnName = "id")
private Person person;
#ManyToOne
#JoinColumn(name = "sessionId", updatable = false, insertable = false, referencedColumnName = "id")
private Session session;
#ManyToOne//(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "personId", updatable = false, insertable = false),
#JoinColumn(name = "areaId", updatable = false, insertable = false),
})
private PersonArea personArea;
}
Code for PersonSession.Id
public static class ID implements Serializable {
private int personId;
private int sessionId;
}
This seems OK, it creates all the correct relationships in the database. The problem comes when I try to insert PersonSession objects - the areaId column is always null, I think that is because it's defined a updatable=false, insertable=false.
However, if I try and make it updatable and insertable, I get an exception complaining the personId is a repeated column:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: foo.bar.PersonSession column: personId (should be mapped with insert="false" update="false")
How can I have the required relationships AND have areaId updatable and insertable?
I should be able to do what I want with this:
#ManyToOne//(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "personId"),
#JoinColumn(name = "areaId", updatable = false, insertable = false),
})
private PersonArea personArea;
But Hibernate does not support mixing updatable and non updatable Join Columns. The accepted answer to this question indicates that it might be supported at some time, but it seems the developers aren't very worried about that shortcoming.
I have how ditched Hibernate in favour of Eclipselink and it works!
I know I am late but I faced the same problem and I used #JoinColumnsOrFormulas to resolve it. Here is what you could do:
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(column = #JoinColumn(name="personId", referencedColumnName = "personId")),
#JoinColumnOrFormula(formula = #JoinFormula(value="areaId", referencedColumnName = "areaId"))})
private PersonArea personArea;
I have two entities, the first of which is:
#Entity
#Table(name="E_CMS_CFG")
public class E_CMS_CFG extends BaseEntity{
#Id
#OneToOne(cascade=CascadeType.ALL)//, fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "CFG_TYPE", nullable = false, referencedColumnName = "CFG_TYPE", columnDefinition = "VARCHAR(32)"),
#JoinColumn(name = "CFG_TYPE_ID", nullable = false, referencedColumnName = "ID")
})
private E_CMS_CFG_TYPE cfgType;
The second entity is:
#Entity
#Table(name="E_CMS_CFG_TYPE")
public class E_CMS_CFG_TYPE extends BaseEntity{
#Id
#Column(name = "CFG_TYPE", length = 32)
private String cfgType;
In the first entity, I use columnDefinition = "VARCHAR(32)" on the CFG_TYPE column; however, in the database, it is creating a cfg_type integer column. Is it possible to create a VARCHAR cfg_type column and not an integer? And if it is, how? I am using Sqlite 3.8.10.1 version.
UPDATE:
The link did not help, but if I replace in E_CMS_CFG the #Id with #NaturalId (mutable = false), it works, but it creates it without a primary key.
You could try to use the natural id annotation here. For example:
#NaturalId (mutable = false)
#Column(name = "CFG_TYPE", unique = true, nullable = false, length = 32)
private String cfgType;
But I think you would still have to provide an id property and that would still be an integer value/column in your database.
I think this Question is a duplicate of "how to use id with string type in jpa hibernate"
I hope that helps!
I am creating entity classes from database (Oracle 11g) in a Java SE 7 application using Netbeans 7.4 RC2. The structure of the tables is defined as below:
The behind the such a cascade definition is to restrict the user from deleting a code if its defined as parent code. But its just a child code then a user can delete it. Furthermore, a CODE_ID can have 0 to 1 parent codes. As you can see the highlighted column doesn't have a parent defined in the CODE_RELATION table.
When I generate the entity classes from the database using the wizard the classes are generated like below:
#Entity
#Table(name = "CODES")
public class Codes implements Serializable {
#Id
#Column(name = "CODE_ID", nullable = false, length = 30)
private String codeId;
#Column(name = "CODE_DESC", length = 200)
private String codeDesc;
#Column(name = "ACTIVE", nullable = false)
private char active;
#JoinTable(name = "CODE_RELATION", joinColumns = {
#JoinColumn(name = "CODE_ID", referencedColumnName = "CODE_ID", nullable = false)}, inverseJoinColumns = {
#JoinColumn(name = "PARENT_CODE_ID", referencedColumnName = "CODE_ID", nullable = false)})
#ManyToMany
private List<Codes> codesList;
#ManyToMany(mappedBy = "codesList")
private List<Codes> codesList1;
//Getter, Setter properties
}
It seems to me a little bit confusing as I can't see the Cascade constraint referenced anywhere in the generated entity. Does this entity exactly reflects my table structure and constraints?
Any suggestions to improve the structure please.
P.S. I am new to java world.
I'm using Hibernate and Oracle database and just got stuck.
I'm trying to map this table:
CREATE TABLE passengers_on_the_flight
(
flight_id NUMERIC(10) REFERENCES flight(flight_id),
passenger_id NUMERIC(20) REFERENCES passenger(passenger_id),
seat NUMERIC(5) NOT NULL,
CONSTRAINT "not free" PRIMARY KEY (flight_id,passenger_id,seat)
);
So my mapping class looks like:
#Entity
#Table(name = "PASSENGERS_ON_THE_FLIGHT")
#NamedQueries({
#NamedQuery(name = "PassengersOnTheFlight.findAll", query = "SELECT p FROM PassengersOnTheFlight p")})
public class PassengersOnTheFlight implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected PassengersOnTheFlightPK passengersOnTheFlightPK;
public PassengersOnTheFlightPK getPassengersOnTheFlightPK() {
return passengersOnTheFlightPK;
}
public void setPassengersOnTheFlightPK(PassengersOnTheFlightPK passengersOnTheFlightPK) {
this.passengersOnTheFlightPK = passengersOnTheFlightPK;
}
#JoinColumn(name = "SEAT", referencedColumnName = "SEAT", insertable = false, updatable = false)
private int seat;
#JoinColumn(name = "FLIGHT_ID", referencedColumnName = "FLIGHT_ID", insertable = false, updatable = false)
#ManyToOne
private Flight flight;
#JoinColumn(name = "PASSENGER_ID",referencedColumnName = "PASSENGER_ID",insertable = false, updatable = false)
#ManyToOne
private Passenger passenger;
//Getters, setters for seat, flight and passanger
And primary key class:
#Embeddable
public class PassengersOnTheFlightPK implements Serializable {
#Column(name = "FLIGHT_ID",nullable=false)
private long flightId;
#Column(name = "SEAT",nullable=false)
private int seat;
#Column(name = "PASSENGER_ID", nullable=false)
private Long passengerId;
//Getters and setters for seat, flightId and passangerId
I tried to persist something and got
ORA-00957: duplicate column name
That because Hibernate generates such query:
insert into PASSENGERS_ON_THE_FLIGHT (seat, FLIGHT_ID, PASSENGER_ID, SEAT) values (?, ?, ?, ?)
I don't get why. Did I mis something in the mapping classes?
I get the same problem with a Coposite Key and solved it adding this params at the #JoinColumn hibernate annotation in the get methods of the external PK duplicate entities:
#JoinColumn(..., updatable=false, insertable=false)
Hibernate really REALLY wants you to have a unique identity key for each table; it has "issues" without it. Try putting an identity key on your table.
You get your error because you specified seat twice. Once you have it as property of your entity PassengersOnTheFlight and second time in the key. If it is part of your key, remove it from the main object. If it is an integer, you probably don't want #JoinColumn for it anyway.