Hibernate avoid nullable unique fields in query? - java

I am having one hibernate pojo class which has 3 fields specified in #UniqueConstraint (unique together) where one of these 3 fields is nullable=true.
When I try to update entry with session.update(pojo) it updates all the entries in database which matches 2 fields (which are not nullable), so does hibernate avoid nullable fields while querying? or there is something what I should know about it?
Edit: Added class
#Entity
#Table (name = "details",
uniqueConstraints = {#UniqueConstraint(columnNames = {"service_id", "billing_item_id", "service_type_id"}, name="UK_name_it")}
)
public class Detail implements Serializable {
#ManyToOne
#JoinColumn(name = "service_id")
#ForeignKey(name = "FK_name2")
#Id
private Service service;
#ManyToOne
#JoinColumn(name="billing_item_id")
#ForeignKey(name = "FK_name3")
#Id
private BillingItem billingItem;
#ManyToOne
#JoinColumn(name="currency_id")
#ForeignKey(name = "FK_name4")
private Currency currency;
#ManyToOne
#JoinColumn(name="service_type_id")
#ForeignKey(name = "FK_name5")
private ServiceType serviceType;
#Column(name = "completed", nullable = false)
private boolean completed;
}

There doesn't seem to be any option like that to have a nullable field in composite key, so I had to end up by adding a integer autoincrement primary key to the table, and keeping service, billingItem and serviceType fields in #UniqueConstraint.
There is another option I could adopt, which is possible in certain scenarios, by adding a serviceType which is considered as All entry (basically when serviceType is null it applies to all the serviceTypes.) and instead of using null for serviceType point to this entry, this way we can have PK and no need to make serviceType a nullable field.

Related

How to map JPA OneToMany relationship when FK is part of CompositeId

So I have a pre-existing Service Entity with multiple OneToMany relationships. Now I need to add one more but I am having trouble and I assume it must be because the Many side uses a Composite Key.
I have the Service.java with its new fields
#Column(name = "TRANSLATION_DV_ID")
private String translationDvId;
#OneToMany(cascade = CascadeType.All, fetch = FetchType.Eager, mappedBy = "service")
private List<Translation> translation;
and
#IdClass(TranslationId.class)
public class Translation {
#Id
#Column(name = "TRANSLATION_DV_ID")
private String translationDvId;
#Id
#Column(name = "LOCALE_CD")
private String localeCd;
#Column(name = "TRANSLATED_NAME")
private String translatedName;
#Column(name = "TRANSLATED_DESC")
private String translatedDesc;
#ManyToOne
#JoinColumn(name = "TRANSLATION_DV_ID", insertable = false, updatable = false)
private Service service;
The test data is generated with sql scripts. I entered the new data and matched the translationDvId's. The data is all present with the correct information except for Translation relationship- each Service always has an empty List<Translation>.
I am not sure what I am missing but here is an example of a data entry
INSERT INTO SCHEMA.SERVICE(SERVICE_CD, TRANSLATION_DV_ID, etc, etc)
VALUES ('servicePrimaryKey', '12345', 'etc, 'etc);
INSERT INTO SCHEMA.TRANSLATION(TRANSLATION_DV_ID, LOCALE_CD, TRANSLATED_NAME, TRANSLATED_DESC)
VALUES ('12345', 'English', 'Guardian', 'Cool stuff');
INSERT INTO SCHEMA.TRANSLATION(TRANSLATION_DV_ID, LOCALE_CD, TRANSLATED_NAME, TRANSLATED_DESC)
VALUES ('12345', 'Spanish', 'Guardia', 'Cosas interesantes');
#JoinColumn has a special property for when there is a Composite PK in the referenced table - referencedColumnName
#OneToMany
#JoinColumn(name = "TRANSLATION_DV_ID", referencedColumnName = "TRANSLATION_DV_ID")
private List<Translation> translation;

Decide which class is owning side in Hibernate

I have Role class, which is the main entity for User's roles and 2 FK in this table, which are pointed on two dictionaries independently: Privelege and Unit classes.
So it's many (Role) to one (Privelege/Unit) relationships as I take.
Qustions:
Is the Hibernate's mapping in my code correct?
Which class is the "owning side" in my case and why?
In which class should I write #JoinColumn and where mappedBy?
As I have read in other posts: "#JoinColumn annotation is maintained in the class which owns the foreign key."
But in Hibernate's doc I see that mappedBy is used on the owning side (see Example 163. Bidirectional #OneToOne).
4. What will happen if I remove some Role records? If I remove some records from dictionaries will it affect Role's records? Can I override this behavior to disable cascading?
I assume that my "Role" class is the owning side because it has FK pointed on 2 dictionaries. So it owns FK. Therefore I need to use #JoinColumn here as its owning side and mappedBy at two dictionaries, because it's mapped by owning side. Am I right.
Update: is the "owning side" synonym to "parent side"?
Role class
#Entity
#Table(name="ROLES_NEW", schema="MAPP")
public class Role implements GrantedAuthority {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false)
private Long id;
#Column(name = "ROLENAME")
private String roleName;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "UNIT_ID")
private Unit unit;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "PRIVELEGE_ID")
private Privelege privelege;
...
}
Privelege class
#Entity
#Table(name="PRIVELEGES", schema="MAPP")
public class Privelege /*implements GrantedAuthority*/ {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false)
private Long id;
#Column(name = "PRIVELEGENAME", length = 255, nullable = false)
private String privelegeName;
#Column(name = "descr", length = 255, nullable = false)
private String descr;
#OneToMany(mappedBy = "privelege")
Set<Role> role;
...
}
Unit class
#Entity
#Table(name="UNITS", schema="MAPP")
public class Unit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false)
private Long id;
#Column(name = "UNITNAME")
private String unitname;
#OneToMany(mappedBy = "unit")
Set<Role> role;
...
}
Yes, the child entity owns the relationship, because that's the side the foreign key is on. This mapping is the most efficient in case of #ManyToOne.
Same thing would apply for many-to-many relationships mapped as two bidirectional #ManyToOne. It can also be done with #JoinTable annotation, but this approach is less efficient.
In case of #OneToOne although child (foreign key holder) owns the relationship, the best performance can be obtained, when using #MapsId and #JoinColumn on the parent side. More about that exception can be found here.
When it comes to mappedBy it's simple - it's used when the relationship is bidirectional and on the side #JoinColumn is not (child has #JoinColumn, parent - mappedBy).
I recommend Vlad Mihalcea's blog when it comes to optimal hibernate mapping: one-to-many, many-to-many.
P.S.: Prefer List to Set to map -to-many relationship (source).

spring-data-jpa 3-way-intersection table

I'll try to illustrate what I'm trying to achieve shortly...
Let's suppose I have a users table:
USER_INFO
USER_ID [PK]
USER_NAME
PASSWORD
an intersection table to define connections for each user (N:M - ManyToMany)
CONNECTION_INFO
CONNECTION_ID [PK]
USER_A_ID [FK - references USER_INFO(USER_ID)]
USER_B_ID [FK - references USER_INFO(USER_ID)]
CONNECTION_TYPE_ID [FK - references CONNECTION_TYPE(CONNECTION_TYPE_ID)]
The CONNECTION_TYPE is simple as:
CONNECTION_TYPE
CONNECTION_TYPE_ID [PK]
CONNECTION_TYPE_NAME [CHECK allowed values are: FRIEND, FAMILY, ...]
On Spring side I defined my User entity as:
#Entity
#Table(name = "USER_INFO")
public class User implements Serializable {
#Id
#NotNull
#Column(name = "USER_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer userId;
#Column(name = "USER_NAME)
private String userName;
#Column(name = "PASSWORD)
private char[] password;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "CONNECTION_INFO",
joinColumns = { #JoinColumn(name = "USER_A_ID") },
inverseJoinColumns = { #JoinColumn(name = "USER_B_ID") })
private List<User> connections;
// ctor, getters, setters, toString, ...
}
I have a UserRepository interface that extends JpaRepository etc etc. Now, this works perfectly and I can retrieve all connections be it FRIEND, FAMILY, MOST_HATED_PERSONS, BLOCKED, DEMON, etc...
I tried to integrate the ConnectionType too in the picture however...
#Entity
#Table(name = "CONNECTION_TYPE")
public class Connection implements Serializable {
public static enum Types {
FRIEND, FAMILY, BLOCKED, ...
}
#Id
#NotNull
#Column(name = "CONNECTION_TYPE_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer connectionTypeId;
#Column(name = "CONNECTION_TYPE_NAME")
private ConnectionType connectionType;
// ctor, getters, setter, etc
}
Now, my question is, how can I get only specific connections for a given user, based on Connection.Types? For example I want to find only FRIENDs, or only FAMILY I think you get my point. This 3 way intersection table gives me one of a headache.
#Clarification:
What I want is a #ManyToMany relation defined on my User entity that happen to have extra column. I know in that case there are proposed solutions like LINK. In my case this extra column is a foreign key to a third table (USER_INFO(Holds the users), CONNECTION_INFO(Holds the connections between users N:M + an info on the type of connection), CONNECTION_TYPE. If I can model it with spring-data-jpa from what I understand I only need a magic named method under UserRepository, something like (totally incorrect):
public interface UserRepository extends JpaRepository<User, Integer> {
List<User> findUserFriendsByConnectionType(User userWhoseFriendsWeAreSearching, String connectionTypeFromTheThirdTable);
}
That's all I want. I know it's simple with a normal extra column by creating an entity for the intersection table too and break the ManyToMany to OneToMany and ManyToOne, it just happens I have a third table and a possibly ManyToOne (1 connection can have 1 associated type, while a type can be linked to any number of connections) on the intersection entity with the connection_type table.
I hope it clears everything up. The above are just a sample I never imagined we'd hang up on an enum because I wanted to make it look simple I possibly made it way too simple perhaps :).
I managed to solve the problem but I'm not sure if this is the right way to do it. Anyway here's my solution. Consider the following 3 tables:
create table USER_INFO (
USER_ID int not null primary key,
USER_NAME varchar(16),
PASSWORD varchar(64)
);
create table CONNECTION_TYPE (
CONNECTION_TYPE_ID int not null primary key,
CONNECTION_TYPE_NAME varchar(16) not null,
CONNECTION_TYPE_DESCRIPTION varchar(128),
unique (CONNECTION_TYPE_NAME)
);
create table CONNECTION (
CONNECTION_ID int not null primary key,
CONNECTION_TYPE_ID int,
RELATED_USER_ID int,
RELATING_USER_ID int,
foreign key (CONNECTION_TYPE_ID) references CONNECTION_TYPE(CONNECTION_TYPE_ID),
foreign key (RELATED_USER_ID) references USER_INFO(USER_ID),
foreign key (RELATING_USER_ID) references USER_INFO(USER_ID)
With the above 3 tables, I want to provide a functionality to get connections for any given user based on the connection's type. For this I created 3 entities as follows:
#Entity
#Table(name = "CONNECTION_TYPE")
public class ConnectionType implements Serializable {
#Id
#NotNull
#Column(name = "CONNECTION_TYPE_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer connectionTypeId;
#NotNull
#Column(name = "CONNECTION_TYPE_NAME", unique = true)
private String connectionTypeName;
#Column(name = "CONNECTION_TYPE_DESCRIPTION")
private String connectionTypeDescription;
...
}
Nothing particularly interesting in here, I omitted the constructor, getters, setters etc and from the ConnectionType I don't want to have a mapping for all connections for this type so that direction is not present.
#Entity
#Table(name = "CONNECTION")
public class Connection implements Serializable {
#Id
#NotNull
#Column(name = "CONNECTION_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer connectionId;
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CONNECTION_TYPE_ID", referencedColumnName = "CONNECTION_TYPE_ID")
private ConnectionType connectionType;
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "RELATED_USER_ID", referencedColumnName = "USER_ID")
private User relatedUser;
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "RELATING_USER_ID", referencedColumnName = "USER_ID")
private User relatingUser;
...
}
This one is more interesting if for noone else at least for me. This would be my intersection table entity. There's the uni-directional mapping for the used ConnectionType with ManyToOne as one Connection can have exactly one ConnectionType while the same ConnectionType can be reused for an arbitrary number of Connections.
The other 2 User mappings I'm sure I've messed up but before that here's the User entity:
#Entity
#Table(name = "USER_INFO")
public class User implements Serializable {
#Id
#NotNull
#Column(name = "USER_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer userId;
#NotNull
#Column(name = "USER_NAME")
private String userName;
#NotNull
#Column(name = "PASSWORD")
private char[] password;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "relatedUser", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Connection> connections;
}
Now here I'm even more sure I completely messed up, but I'll show the actual error. My repository is simple as a brick:
#Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
And I have a UserService with a simplified findAllConnectionsForUserById function:
#Service
public interface UserService {
List<User> findAllConnectionsForUserById(Integer userId);
}
The method implementation is simple enough:
#Override
#Transactional
public List<User> findAllConnectionsForUserById(Integer userId) {
Optional<User> _user = userRepository.findById(userId);
// omitted exception handling...
User user = _user.get();
List<Connection> connections = user.getConnections();
return connections.strea.map(Connection::getRelatingUser).collect(Collectors.toList());
This way it seem to work fine for the simple case and in case I take the ConnectionType too:
connections.stream().filter(c -> c.getConnectionType().getConnectionTypeName().equals("FRIEND")).map(Connection::getRelatingUser).collect(Collectors.toList());
it seem to work as well. Again, not sure if this is the right way but at least it does the job.

Populate table in java using spring mvc

I have three entities: Account, Partner, and Referral.
The Partner table is already full and has a link to the Account table.
When registering a user, the Account table is filled out.
Then I need to fill out the Referral table in which there are links to the Account and the Partner.
In this case, I need to check if there is a Referral link in the request, I need to check that it is in the Partner table and write to the ID Referral table of the Partner. And also take the ID from the table Account and also write it to the Referral table.
I have entity and controller
#Entity
#Table(name = "partner")
public class Partner implements Serializable {
#Id
#Column(name = "id", unique = true, nullable = false)
private Long id;
#OneToOne
#JoinColumn(name = "account_id")
private Account account;
#Column(name = "referral_link", nullable = false, unique = true)
#NotEmpty
private String referralLink;
Getter, Setter, and Constructor
#Entity
#Table(name = "referral")
public class Referral implements Serializable {
#Id
#Column(name = "id", unique = true, nullable = false)
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "partner", cascade = CascadeType.ALL)
#JoinColumn(name = "partner_id")
private Set<Partner> partnerId = new HashSet<>();
#OneToOne
#JoinColumn(name = "account_id", nullable = false, unique = true)
private Account accountId;
#Column(name = "device_id")
private String deviceId;
#Column(name = "setup_date", nullable = false)
private Date setupDate;
Getter, Setter, and Constructor
In the Controller, I wrote this code:
Long defaultId = 6L;
if (referralLink == null) {
referral = new Referral(defaultId, account.getId();
referralService.create(referral);
} else {
List<Partner> partnerList = partnerService.getAll();
if (partnerList.contains(referralLink)) {
// How to get partnerId?
referral = new Referral(partnerId, account.getId();
} else {
referral = new Referral(defaultId, account.getId();
referralService.create(referral);
}
referralService.create(referral);
}
Many questions turned out:
How to get the element's id on the sheet that the referralLink belongs to?
How to add a default ID if the referralLink is empty?
When creating a Referral, he asks me for an entity Account instead of my Long and Set partners, what am I doing wrong?
You have many problems,
First in your Refferal class
#OneToMany(fetch = FetchType.LAZY, mappedBy = "partner", cascade = CascadeType.ALL)
#JoinColumn(name = "partner_id")
private Set<Partner> partnerId = new HashSet<>();
you cannot have JoinColumn at the two sides of the relationship, since this is a bidirectional relation you should have joincolumn in one side and mappedBy in the other side.
In your persistence logic you should have a reference of each side of the relation in the other side since the relation is bidirectional, I am talking about the Refferal and Partner.
How to get the element's id on the sheet that the referralLink belongs
to?
I am not sure what you mean by element's id but I suppose It is entity which has association with Partner, in this case you only need to find the Partner for a specific referralLink and use normal simple getters
How to add a default ID if the referralLink is empty?
simply check if the referralLink is empty and add an id, if you are talking about the autogeneratedId then why would u need to add such thing and even if you do it will be just ignored, if you are talking about id of another entity (foreign key) set the entity you need if the referralLink.
When creating a Referral, he asks me for an entity Account instead of
my Long and Set partners, what am I doing wrong?
In your referral entity you are specifying not null constraint on the account
#OneToOne
#JoinColumn(name = "account_id", nullable = false, unique = true)
private Account accountId;
so just remove "nullable = false"

JPA Compound key with #EmbeddedId

In a legacy database, I have three tables: Users, Workgroups, and UsersWorkgroup. UsersWorkgroup stores what role a user has in a workgroup.
Here are the relevant code snippets:
#Entity
#Table(name = "users_workgroup")
public class UsersWorkgroup implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected UsersWorkgroupPK usersWorkgroupPK;
#JoinColumn(name = "idworkgroup", referencedColumnName = "idworkgroup")
#ManyToOne(optional = false)
private Workgroup workgroup;
#JoinColumn(name = "user_name", referencedColumnName = "user_name")
#ManyToOne(optional = false)
private Users users;
#Column(name = "role")
private Integer role;
#Embeddable
public class UsersWorkgroupPK implements Serializable {
#Basic(optional = false)
#Column(name = "idworkgroup", insertable=false, updatable=false)
private int idworkgroup;
#Basic(optional = false)
#Column(name = "user_name", insertable=false, updatable=false)
private String userName;
#Entity
#Table(name = "workgroup")
public class Workgroup implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idworkgroup")
private Integer idworkgroup;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idworkgroup")
private Collection<UsersWorkgroup> usersWorkgroupCollection;
And of course, problem is, it doesn't work.
Currently I get this exception:
Exception Description: An incompatible
mapping has been encountered between
[class entity.Workgroup] and [class
entity.UsersWorkgroup]. This usually
occurs when the cardinality of a
mapping does not correspond with the
cardinality of its backpointer.
Which I don't understand since OneToMany should match ManyToOne... Or is it a ManyToMany relationship? If I switch to #ManyToMany, I get this:
Exception Description: The target
entity of the relationship attribute
[workgroup] on the class [class
com.ericsson.rsg.ejb.entity.UsersWorkgroup]
cannot be determined. When not using
generics, ensure the target entity is
defined on the relationship mapping.
I'm trying to understand compound keys (embedded), but all the examples I could find have only simple columns that are not foreign keys (but that's the whole point of a compound key, isn't it?). Can the UsersWorkgroup table secretly be a join table?
Should I declare the PK class as a strict POJO class? Or should I put the #JoinColumn annotations in the PK class? How do I refer to the columns within the compound key from another table? Should I initialize the PK object in the refering class constructor, or is it not necessary?
I feel stuck completely.
First of all, I think your relation is a Many To Many, as a user can be in many groups, and a group can have many users (or I would assume so).
Second, as far as I know you have to reference both id_workgroup and user_name as JoinColumns, because they are part of the PK and a unit, so both should be referenced.
Also, I see the "equals" and "hashCode" methods missing from your embedded PK, as well as the getters/setters. I believe they are mandatory.
Your mapping looks fine except for mappedBy - it should be a property name, not a column name:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "workgroup")
private Collection<UsersWorkgroup> usersWorkgroupCollection;

Categories