Spring: Is there any annonation about not repeadable data? - spring

For example I have a category entity:
#Data
#Entity
#Table(name="categories")
#AllArgsConstructor
#NoArgsConstructor
public class Category{
#Id
#Column(name="id")
private int id;
#Column(name="category_name")
private string name;
}
I want an annonation to when I try to save a category with name currently exits in category table name column, not to allow to do.
if there is no annonation, how can i not allow data to repeat

#Column(unique = true) is a way to go.

Related

JPARepository CPRQ modified does not save full object

I have modified the design of CPRQ a bit to help my database pattern
I have an Employee table and a Department table. Both have common properties
#Column(name="tenantIDPKFK")
private Integer tenantIdpkfk;
#Column(name="status")
private Integer status;
So I created a base class ABaseEntity like below
public class ABaseEntity {
public ABaseEntity() {
}
public ABaseEntity(int tenantIdpkfk, int status) {
this.tenantIdpkfk = tenantIdpkfk ;
this.status = status ;
}
#Column(name="tenantIDPKFK")
private Integer tenantIdpkfk;
#Column(name="status")
private Integer status;
I have extended EmployeeEntity with ABaseEntity
#Entity
#Table(name = "employee")
public class EmployeeEntity extends ABaseEntity{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "first_name")
#NotEmpty(message = "Please provide a name")
#NotBlank
private String firstName;
My CommandHandler runs the following code
EmployeeEntity savedEmployeeEntity = this.employeeRepository.saveAndFlush(employee);
this.mediator.emit(new EmployeeCreatedEvent(savedEmployeeEntity.getId()));
Database saved the object, but only id, firstname. Does not save tenant and status columns.
I know I am missing something silly. Please help.
EDIT
Adding #MappedSuperclass to the ABaseEntity class fixed the issue.
#MappedSuperclass
public class ABaseEntity {...}
Database saved the object, but only id, firstname. Does not save
tenant and status columns.
By default JPA doesn't consider the parent class in the orm (object-relational mapping) of the current class.
You have to specify on the parent class #Inheritance with the strategy to use or use the default one.
For example :
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class ABaseEntity {...}
More info here.

Composite key (EmbeddedId) with JoinColumn, select

I have a pretty annoying use case that is a puzzle.
I have the following composite key (Embeddable):
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#Getter
#Embeddable
public class DefaultFilterId implements Serializable {
#Column(name = "AUTOM_PLAN_TYPE_TCD_ID")
Long planTypeId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "FILTER_TYPE_TCD_ID", referencedColumnName = "FILTER_TYPE_TCD_ID")
FilterTypeEntity filterType;
#Column(name = "VOLGORDE")
Long order;
}
My entity looks like this:
#Entity
#Table(name = "ZVZ_PLAN_T_FLTR_T_DEF")
#AllArgsConstructor
#NoArgsConstructor
#Getter
public class DefaultFilterEntity {
#EmbeddedId
private DefaultFilterId defaultFilterId;
private Long planTypeId;
#MapsId("filterTypeId")
private Long filterType;
private Long order;
}
Basically I want a composite primary key on the 3 fields, not sure how to use mapsId on the id (Long value) of the FilterType.
The JPA query is simple:
#Repository
public interface DefaultFilterRepository extends JpaRepository<DefaultFilterEntity, Long> {
List<DefaultFilterEntity> findDefaultFiltersByPlanTypeId(Long planId);
}
It's a pain really, almost on the brink of just writing an SQL query which would take me 2 seconds :-/
Anyhow the query fails because the select query isn't correctly generated; in the hibernate log I've seen that the property names are included in the select, while they obviously don't exist.
So how do I do this?
Edit: I gave up on the convoluted embeddable shizzle.
Went with:
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#Getter
public class DefaultFilterId implements Serializable {
Long planTypeId;
FilterTypeEntity filterType;
Long order;
}
#Entity
#Table(name = "ZVZ_PLAN_T_FLTR_T_DEF")
#IdClass(DefaultFilterId.class)
#AllArgsConstructor
#NoArgsConstructor
#Getter
public class DefaultFilterEntity {
#Id
#Column(name = "AUTOM_PLAN_TYPE_TCD_ID")
private Long planTypeId;
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "FILTER_TYPE_TCD_ID", referencedColumnName = "FILTER_TYPE_TCD_ID")
FilterTypeEntity filterType;
#Id
#Column(name = "VOLGORDE")
private Long order;
}
This seems to generate the correct hibernate query, however it returns an empty list from my jpa query. When performing the hibernate query directly on the db it shows the correct resultset.
(Well correct, not really, as I don't see JOIN being made to get the joined filterTypes).
Any idea?
Fixed this issue by adding a surrogate key to the table.
Hibernate is pleased, and all worked instantly.
In case devs don't have ability to add a surrogate key to the table for some reason, I wish you good luck.

Spring - JPA join abstract class in abstract class

I have a problem with JPA inheritance. The database model is also specially built. It contains several tables with the same attributes (the tables were intentionally cut by country) and all these tables connect to another table (OneToOne).
Here is an example of the data model:
usa_user, germany_user, austria_user. All these tables have the same attributes (id, name, address). Now the address was also built up according to the countries e.g. usa_address, germany_address, austria_address.
Now I don't know or have the problem that I have been mapping them correctly for a long time. I have the following:
// All Lombok Getter, Setter Args,...
#MappedSuperclass
public abstract Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", referencedColumnName = "id")
#JsonIgnore
private User user;
private String name;
private String addr_num;
...
}
// All Lombok Getter, Setter Args,...
#MappedSuperclass
public abstract User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#JsonIgnore
private Address address;
private String name;
}
#Entity
#Table(name = "usa_user")
public class UsaUser extends User {}
#Entity
#Table(name = "austria_user")
public class AustriaUser extends User {}
#Entity
#Table(name = "germany_user")
public class GermanyUser extends User {}
#Entity
#Table(name = "usa_address")
public class UsaAddress extends Address {}
#Entity
#Table(name = "austria_address")
public class AustriaAddress extends Address {}
#Entity
#Table(name = "germany_address")
public class GermanyAddress extends Address {}
But unfortunately this does not work. Every time I start it JPA notices that it can't map the Entities Address - User (which is understandable because they are not entities but abstract classes). What would be the best way to solve this? I want to avoid that I have to list the attributes in all these entities because it would be redundant.
The goal is to find out how I can use a #MappedSuperclass in a #MappedSuperclass.
MappedSuperclass is not queryable and thus also not joinable. You need to map this as an abstract entity with the table per class inheritance strategy. Just switch to #Entity on the Address and User and add #Inheritance(TABLE_PER_CLASS).

Two tables connected via Primary Key

I have read about the use of #MapsId and #PrimaryKeyJoinColumn annotations, which sounds like a great options. I have two tables (UserList and UserInformation) which have a child, parent relationship, respectively; both classes below are abbreviated to just include the relevant columns. UserInformation's primary key value is always null and does not take the value of its parent column.
User Class
#Entity
#Data
#Table(name = "user_list")
public class UserList {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// List of foreign keys connecting different entities
#OneToOne(mappedBy = "user")
#MapsId("id")
private UserInformation userInfo;
}
UserInformation Class
#Entity
#Data
#Table(name = "user_information")
public class UserInformation implements Serializable {
#Id
private Integer userId;
#OneToOne
private UserList user;
}
I would prefer to not use an intermediary class if possible. I'm not tied to MapsId or even this implementation if there is a better solution.
Thanks!
The question is not very clear to me, but I think you could improve the following in the modeling of the entity:
The #column annotation can only be omitted when the class parameter is called exactly the same as the database column, taking into account the table name nomenclature, could it be that the column is user_id ?, if so the id parameter should be :
#Id
#column(name="USER_ID")
private Integer userId;
In the user entity being id, it will match the DB ID field so the #column annotation is not necessary

JPQL query / JPA / Spring boot best practice of updating many to many table

I have a user table and a city table and I have a connecting table users_cities (columns: id, user_id, city_id). A user can follow multiple cities.
From the client side i send an array of cityIds. Some might be new some might still be selected and some might have been deselected.
What is a best practice to update the users_cities table with the data? Should I just delete everything for that particular userId and insert the new array or ... ?~
Also, how does one delete and repsectively insert the data in bulk, for a many to many reference?!
#Getter
#Setter
#NoArgsConstructor
#ToString
#Accessors(chain = true)
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
private String email;
private String password;
private Boolean isGuest;
#ManyToMany(fetch = FetchType.EAGER)
private Set<Role> roles;
#ManyToOne()
#JoinColumn(name = "country_following_id")
private Country countryFollowing;
#ManyToMany()
private Set<City> citiesFollowing;
#ManyToMany()
private Set<Offer> offersFollowing;
}
and
#Getter
#Setter
#NoArgsConstructor
#ToString
#Accessors(chain = true)
#Entity
#Table(name = "cities")
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#NonNull
private Long id;
private String countryCode;
private String name;
#Latitude
private Double latitude;
#Longitude
private Double longitude;
}
Should I just delete everything for that particular userId and insert the new array
Since the relation connecting users and cities does not have an identity of its own, that sounds reasonable
Also, how does one delete and repsectively insert the data in bulk, for a many to many reference?
Just clear the User.citiesFollowing collection and populate it anew (hint: since you have cityIds at the ready, you can use EntityManager.getReference() to load the cities)

Resources