Nullable Map mapping in jpa - java

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.

Related

JPA, How to add the value of entity type in Map to primary key?

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.

Difference between name and referencedColumnName in #JoinColumn annotation?

#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

JPA Unknown Column in Field List

I have a relation between Accommodation and Booking classes, and also I set a foreign key in booking table.
ALTER TABLE `project`.`booking`
ADD INDEX `fk_accId_fk_idx` (`accommodation` ASC);
ALTER TABLE `project`.`booking`
ADD CONSTRAINT `fk_accId_fk`
FOREIGN KEY (`accommodation`)
REFERENCES `project`.`accommodation` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
Accommodation class:
#Entity
....
public class Accommodation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private BigInteger id;
#OneToMany(mappedBy = "accommodation", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
#JsonManagedReference
private List < Booking > bookings;
......getters setters
}
Booking class:
#Entity
public class Booking implements Serializable {
......
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "bookings", nullable = true)
#JsonBackReference
private Accommodation accommodation;
....getters setters
}
When I execute a query for listing accommodations, I get unknown column in field list error.
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'bookings7_.bookings' in 'field list'
Even I set the relation and define the foreign key in table, what is the reason that I get this error?
Try to define your join-table mapping manually in JPA. Drop your schema and let JPA create your tables:
Accommodation class
#OneToMany(mappedBy = "accommodation", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
#JsonManagedReference
private List < Booking > bookings;
Booking class
#ManyToOne(fetch = FetchType.EAGER)
#JoinTable(name = "accommodation_booking_join_table",
joinColumns = {#JoinColumn(name="booking_id")},
inverseJoinColumns = #JoinColumn(name = "accommodation_id"))
#JsonBackReference
private Accommodation accommodation;
Try changing your column names to lower case in your db and entity class.
I had a situation like that, and I solved it by changing the field's position on the query. Looks like it's a MySQL bug, like (or the same as) the one mentioned on this post:
https://bugs.mysql.com/bug.php?id=1689
The description of this MySQL bug mentioned a similar workaround solution: "I found that by swapping that field's position in the table with another field that it worked OK."

Add Primary key on ManyToMany (for Publication)

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.

How to generate some JPA entities with a multi join?

i would like to create an application in this context : Zk 6, Spring v3.1.1, JPA 2.0, Hibernate 4.1.4, all with annotations but i have some pb with JPA concept.
Here are a type of case study :
3 tables, all linked via a join table ; we are dealing with cardinality 0, n.
So we have T_E_USER, T_E_TYPE and T_E_AIR.
Each table has a numeric ID, and a simple VARCHAR field.
A join table is created with T_J_USR_TPE_AIR with the 3 ID referenced by foreign keys forming a composed primary key.
I'm using Hibernate Tools for generate my entities (version JPA).
And that's where the problems start ....
I have, in each entity class, an attribute of type set with annotation # OneToMany.
I have a class representing the join that has an id attribute of complex type (another class) with an annotation EmbeddedId for a composite key.
And attributes representing the three entities with annotations # ManyToOne.
Here are my questions, because that's where I'm confused:
which should i set into the "mappedBy" attribute in the annotation # OneToMany of my entities?
Am I forced to do a class entity representing the join?
How does the CASCADE? Is it possible to use it in this context to enrich the join table "automatically"? Or should I manually instantiate the class representative of the join in order to persist the information myself?
A big thank you in advance for any kind soul who could give me a helping hand.
Thank you for your answers but one said "yes" when the other says "no" lol
Here's what I did during the day but I have not yet been tested.
In each entity table, i added a #OneToMany relation with mappedBy setted to the attribute defined in "join" entity :
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "aircraft",
cascade = { CascadeType.REMOVE })
private Set<UserConfig> userConfigs = new HashSet<UserConfig>(0);
...
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "userAccount",
cascade = { CascadeType.REMOVE })
private Set<UserConfig> userConfigs = new HashSet<UserConfig>(0);
...
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "referenceType",
cascade = { CascadeType.REMOVE })
private Set<UserConfig> userConfigs = new HashSet<UserConfig>(0);
And i created a new Entity for the join table.
#Entity
#Table(name = "T_J_USR_RFT_AIR_URA")
public class UserConfig implements java.io.Serializable {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "airId",
column = #Column(name = "URA_AIR_ID", nullable = false)),
#AttributeOverride(name = "usrId",
column = #Column(name = "URA_USR_ID", nullable = false)),
#AttributeOverride(name = "rftId",
column = #Column(name = "URA_RFT_ID", nullable = false))
})
private UserConfigId id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "URA_RFT_ID", nullable = false, insertable = false, updatable = false)
private ReferenceType referenceType;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "URA_USR_ID", nullable = false, insertable = false, updatable = false)
private UserAccount userAccount;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "URA_AIR_ID", nullable = false, insertable = false, updatable = false)
private Aircraft aircraft;
...
getter & setter
}
Where UserConfigId is :
#Embeddable
public class UserConfigId implements java.io.Serializable {
#Column(name = "URA_AIR_ID", nullable = false)
private Integer airId;
#Column(name = "URA_USR_ID", nullable = false)
private Integer usrId;
#Column(name = "URA_RFT_ID", nullable = false)
private Integer rftId;
...
getter & setter
}
What do you think about this practice ?
I just used "cascade" if an object of the join table is deleted in order to delete all element associated in the join.
Is it all right ?
Anyway thank you Tom, i will analyzed your link.
Thank you JMelnyk too.
You are welcome if you want to demonstrate what are the best practices for this case.
Three-way joins are tricky. I think what you've done, using an entity for the join table, is probably the right thing to do. To answer your questions:
Your #OneToMany attributes refer to the entity mapping the join table; they should be mappedBy the appropriate #ManyToOne attribute in that entity.
Yes, unfortunately, an entity for the join table is the best way to do this.
Cascades can be used to automatically add objects to the database, but not to create objects. You will need to create instances of the join entity in code.
which should i set into the "mappedBy" attribute in the annotation #
OneToMany of my entities?
mappedBy attribute represents a property name you are joining on. Read more...
e.g. AnyEntity holds List<Employee> which is joined on (mappedBy) department property in Employee entity, and that department property holds the association.
Am I forced to do a class entity representing the join?
No, you do not provide an entity class for join tables.
How does the CASCADE? Is it possible to use it in this context to
enrich the join table "automatically"? Or should I manually
instantiate the class representative of the join in order to persist
the information myself?
Yes it is possible to enrich associations of the entity and itself by marking associations with desired cascade type.
e.g. We have a Department which holds List<Employee> and I put CascadeType.PERSIST on employees. Now we populate department objects with its properties and employees. When we are finished, we persist only the department, and it will cascade operation to employees.

Categories