I have a ManyToMany relation between two entity
#javax.persistence.Entity
#Table(name = "t_aircraft_model")
public class AircraftModel extends DbObject {
#ManyToMany(fetch=FetchType.LAZY)
#JoinTable(name = "t_aircraft_model_entity", joinColumns = { #JoinColumn(name = "aircraft_model_uid", nullable = false) }, inverseJoinColumns = { #JoinColumn(name = "entity_id_LDAP", nullable = false) })
private List<com.airbushelicopter.ahead.db.pojo.Entity> entities ;
But sqlServer doesn't allow me to publish the intermediate table : t_aircraft_model_entity
I thought about 2 solutions
Both column of the table the t_aircraft_model_entity become the primary key (ok in my case a aircraft can't be linked multiple time to the same entity)
I add a 3rd column (id) which will be the primary key
Or ?
But I have no idea how I can do this with hibernate and annotation.
thanks !
First things first. You will need 3 tables to make a many to many relation, of course, you will need to make sure that both of your other tables have a PK
On the code side, you can do like this:
Your Airplace Model:
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "t_aircraft_entity_relation",joinColumns = {
#JoinColumn(name = "aircraftid", nullable = false, updatable = false)},
inverseJoinColumns = { #JoinColumn(name = "entityid",nullable = false,updatable= false)
})
private Set<com.airbushelicopter.ahead.db.pojo.Entity> entities ;
On your Entity Model:
#ManyToMany(fetch = FetchType.EAGER,mappedBy="entities")
private Set<AircraftModel> aircrafts;
And you will have to create a relation table, like in my example:
CREATE TABLE t_aircraft_entity_relation
(
aircraftid integer NOT NULL,
entityid integer NOT NULL,
CONSTRAINT "PK_AIRCRAFT_ENTITY" PRIMARY KEY (aircraftid, entityid),
CONSTRAINT "FK_AIRCRAFT_ENTITY" FOREIGN KEY (aircraftid)
REFERENCES t_aircraft_model (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT "FK_ENTITY_AIRCRAFT" FOREIGN KEY (entityid)
REFERENCES t_entity_model (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
PS: This piece of SQL is based on Postgresql, so you will have to do a little bit of change.
Related
I have 3 entity classes like below:-
Role Entity
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany
#LazyCollection(LazyCollectionOption.FALSE)
#JoinTable(name = "roles_privileges", joinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "privilege_id", referencedColumnName = "id"))
private Set<Privilege> privileges;
// getters, setters etc
}
Privilege Entity
#Entity
public class Privilege {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToMany(mappedBy = "privileges", fetch = FetchType.EAGER)
#JsonIgnore
private Set<Role> roles;
// getters, setters etc
}
UrlsMapper Entity
#Entity(name = "urls_mapper")
#Table(name = "urls_mapper")
public class UrlsMapper {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
#Column(name = "http_method")
private HttpMethod httpMethod;
#Column(name = "path")
private String path;
#ManyToMany(fetch = FetchType.EAGER)
#MapKeyJoinColumn(name = "role_id", referencedColumnName = "id")
#JoinTable(
name = "u_r_p",
inverseJoinColumns = #JoinColumn(name = "privilege_id")
)
Map<Role, Privilege> privilegeMap;
// getters, setters etc
}
The keys, primary and foreign that get created are as below
The logs while table generation is as below:-
Hibernate: create table u_r_p (urls_mapper_id bigint not null, privilege_id bigint not null, role_id bigint not null, primary key (urls_mapper_id, role_id)) engine=InnoDB
Hibernate: alter table u_r_p add constraint FKgd7gd9f9ded1s28swdudqs0ro foreign key (privilege_id) references Privilege (id)
Hibernate: alter table u_r_p add constraint FKrryprkx4j60lyjti16eysn5g5 foreign key (role_id) references Role (id)
Hibernate: alter table u_r_p add constraint FKfkthdnoca59a18ba96183p7ov foreign key (urls_mapper_id) references urls_mapper (id)
And I just want to know how can I add the privilege_id also into the JoinTable u_r_p and if there can be other best options for this. Manually doing in the database is a obvious alternate, but i wanted to know the hbm2ddl.auto based solution, so that code manages it itself
I don't think you've modeled your concepts properly. You have a ManyToMany between Role and Priviledge but what makes UrlMapper an entity? You have a Map<Role, Privilege> field in UrlMapper but that is the purpose of the join table so there should be no need to duplicate that. Instead it seems to be that HttpMethod and Path are attributes of the relationship.
However, I might also note that you seem to be expecting there be a Role/Privilege join for many different HttpMethod/Path combinations. This seems incredibly fine grained and an operations nightmare, but whatever. Anyway, what you seem to be saying is you want unique combinations of Role/Privilege/HttpMethod/Path so you should just make a entity for that and the table represents your set. Make a Permission entity that holds a unique Role/Privilege/HttpMethod/Path. Role, Privilege, HttpMethod, and even Path are essentially enumerations so you should have a table for each for each of them with ManyToOne mappings in the Permission entity. You could add bidirectional OneToMany mappings in each of the lookup tables but I'm not sure I see a need for that. It's up to you.
I assume Privilege would be {allow, deny} but it seems like less of a tangle if you assume deny unless a Role/HttpMethod/Path permission specifically exists. If that's the case then I would leave out the Privilege entity. Anyway, just a thought. Hope this helps.
This is a shortened version of the entities where I only show the relevant parts.
#Entity
#Data
public class Wrapper {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id
#OneToOne(mappedBy = "wrapper", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Application application;
public Wrapper(Application application) {
this.application = application;
application.setWrapper(this);
}
}
#Data
#Entity
#EqualsAndHashCode(exclude = "wrapper")
public class Application {
#Id
private Integer id;
#JsonIgnore
#OneToOne
#JoinColumn(name = "id")
#MapsId
private Wrapper wrapper;
#OneToMany(mappedBy = "application", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
#SortNatural
private SortedSet<Apartement> ownedApartements = new TreeSet<>();
}
#Entity
#Data
public class Apartement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "application_id", insertable = false, updatable = false)
private Application application;
}
#Repository
public interface WrapperRepository extends JpaRepository<Wrapper, Integer> {
}
The above entities generates the following create table statements:
create table Wrapper (
id int identity not null,
primary key (id)
)
create table Application (
id int not null,
primary key (id)
)
create table Apartement (
id int identity not null,
application_id int not null,
primary key (id)
)
alter table Apartement
add constraint FKsrweh1i1p29mdjfp03or318od
foreign key (application_id)
references Application
alter table Application
add constraint FKgn7j3pircupa2rbqn8yte6kyc
foreign key (id)
references Wrapper
Given the follow entities and the following code:
Apartement apartement1 = new Apartement()
Apartement apartement2 = new Apartement()
Wrapper wrapper = new Wrapper(new Application());
Application application = wrapper.getApplication();
application.getOwnedApartements().addAll(Arrays.asList(apartement1, apartement2));
apartement1.setApplication(application);
apartement2.setApplication(application);
WrapperRepository.saveAndFlush(wrapper);
I see three inserts in the log.
First wrapper, then application, and finally apartement. But for some reason application_id is null on the first save. But I know it has a bi-directional relationship.
The error I get is:
Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "APPLICATION_ID"; SQL statement:
insert into Apartement (id) values (null) [23502-197]
Why does this happen? Do I need to store everything in the correct order? Do I need to first store wrapper and application, then finally store the apartement once I have application ID?
Cannot hibernate store all three in one go? Or figure this out it self?
Sorry I fixed it.
The problem was
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "application_id", insertable = false, updatable = false)
private Application application;
I removed insertable = false, updatable = false and added optional=false
That worked
#JoinColumn(name = "application_id", optional = false)
Try this:
Apartement apartement1 = new Apartement()
Apartement apartement2 = new Apartement()
Wrapper wrapper = new Wrapper(new Application());
Application application = wrapper.getApplication();
application.getOwnedApartements().addAll(Arrays.asList(apartement1, apartement2));
apartement1.setApplicationId(application.getId());
apartement2.setApplicationId(application.getId());
WrapperRepository.saveAndFlush(wrapper);
#ManyToOne
#JoinColumn(name = "someValue" , referencedColumnName = "someOtherValue" )
What values are to be placed in name and referencedColumnName column if 2 tables are linked by ManyToOne association?
Suppose you have Two tables:
1. Department table with columns:
a. Dept_ID (primary key)
b. Dept_Name
2. Employee Table with following column:
a. Emp_Id (primary key)
b. Dept_Id_fk (foreign key)
c. Salary
Now your join column for Employee Entity class will be
#ManyToOne
#JoinColumn(name = "Dept_Id_fk", referencedColumnName = "Dept_ID")
Department department;
So referencedColumnName means column that you are referencing to from your foreign key column.
How would the join column look like for oneToMany association? Here is an example:
Person table:
person_id (pk), person_name
person_reltn table:
person_reltn_id (pk),
child_person_id (fk),
parent_person_id (fk)
For the above tables, if I were to create the Person entity:
class Person(){
#Id
#Column(name = "PERSON_ID")
private long personId;
#NotFound(action = NotFoundAction.IGNORE)
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(
name = "CHILD_PERSON_ID",
referencedColumnName = "PERSON_ID",
updatable = false,
insertable = false)
#Where(clause = "ACTIVE_IND = 1")
#Filter(
name = FilterConstants.END_EFFECTIVE_DATE_TIME_FILTER,
condition = FilterConstants.END_EFFECTIVE_DATE_TIME_CONDITION)
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private final List<PersonRelation> personRelations = new ArrayList<>();
}
In the joinColumn, should the name always have the foreign key (which means the value from entity you are joining to) and the referenceColumnName should alway have the primary key on the entity? If yes, it will be the opposite of Sayantan's response above. Please let me know if I misunderstood the concept.
Update on 03/04/2021
After doing more research, I found the documentation on how to set the referenceColumnName based on the entity mappings[1]. Looks like, for unidirectional OneToMany mapping, the referenced column is in the table of the source entity.
1.https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/JoinColumn.html
I have been migrating from hibernate 4 to hibernate 5. It is ok in hibernate 4, but doesn't work in hibernate 5.
I am getting exception:
Caused by: org.hibernate.MappingException: Foreign key
(FKf6eo63yo42ylh7vl5klap2eum:ProductParent [parent_id])) must have
same number of columns as the referenced primary key (ProductParent
[parent_id,product_id])
This is my hibernate mapping:
#Entity
public class ProductParent implements Serializable {
#Id
#OneToOne()
#JoinColumn(name = "product_id")
private AbstractProduct product = new AbstractProduct();
#ManyToMany(cascade = ALL, fetch = EAGER)
#JoinTable(name = PRODUCTPARENT, joinColumns = { #JoinColumn(name = "product_id") }, inverseJoinColumns = { #JoinColumn(name = "parent_id") })
private Set<ProductParent> parents = new HashSet<>();
and table structure:
CREATE TABLE productparent (
product_id bigint NOT NULL,
parent_id bigint,
CONSTRAINT fk_parent_id FOREIGN KEY (parent_id) REFERENCES abstractproduct (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_product_id FOREIGN KEY (product_id) REFERENCES abstractproduct (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT product_id_parent_id_should_be_unique UNIQUE (product_id, parent_id)
)
Could you help me that?
Solution:
To add:
#OneToOne
#JoinColumn(name = PARENT_ID)
private AbstractProduct parent = new AbstractProduct();
I'm trying to map entity-key with entity-value map to database using JPA2.1 and Hibernate 4.3.7.
Here's my code:
#Entity
#Audited
class Form{
//id
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "form_content",
joinColumns = #JoinColumn(name = "id_form", nullable = false),
inverseJoinColumns = #JoinColumn(name = "id_field_content"/*non-working part start*/, unique = false, nullable = true/*non-working part stop*/),
uniqueConstraints = { #UniqueConstraint(columnNames = { "id_field", "id_form" }) })
#MapKeyJoinColumn(name = "id_field", nullable = false)
private Map<FormEntryDefinition, FormEntryValue> content;
//getters, setters, equals, etc.
}
Hibernate generates table
form_content(
id_form bigint primary key not null,
id_field_content bigint <!-- problem start -->not null unique <!--problem stop -->,
id_field bigint primary key not null)
with proper foreign keys in all 3 fields.
Could anyone tell me, why hibernate generates unique and not null constraints, so I can't persist map with nullable value?
Is there any workaround for that problem?
The unique constraint is due to using a #OneToMany association. If you change it to #ManyToMany then the unique constraint would no longer be necessary.
The join table FK columns only make sense for non-nullable columns. A row in the join table is a link between two tables, and if one FK is missing then the association breaks anyway and it's equivalent to not having a link row in the first place.