I have following connection between tables Image:
#Entity
#Table(name = "image")
#Data
public class Image {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
private byte[] image;
#OneToOne(mappedBy = "avatar")
private Personal personal;
}
and Personal
#Entity
#Table(name = "personal")
#Data
public class Personal {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String position;
private String phone;
private String email;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "avatar_id", referencedColumnName = "id")
private Image avatar;
}
I want to get back from my service Personal entities with ONLY id field from Image table. Repositories and services are standard from tutorials - without extra code or overrides
If read-only is ok use a DTO or interface projection.
Related
I am learning JPA.
I need to create 3 tables, product (pk => id), cart (pk => id), cart_details (pk also fk => product_id, cart_id).
The relation is : One cart can contain multiple cart_details, one cart_details can contain multiple product and one product can be put on multiple cart_details. I need only 3 tables, but JPA creates 4 tables for me: product, cart, cart_details, cart_details_product
#Entity
#Table(name = "product")
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#NotBlank
#Size(max = 50)
private String name;
#Size(max = 300)
private String description;
#NotNull
private Double price;
private int qty;
#Column(name = "created_date")
private Date createdDate;
#Column(name = "updated_date")
private Date updatedDate;
}
#Entity
#Table(name = "cart")
public class Cart implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "total_price")
private double totalPrice;
#Column(name = "created_date")
private Date createdDate;
#Column(name = "updated_date")
private Date updatedDate;
}
#Entity
#Table(name = "cart_details")
public class CartDetails implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private CartDetailsId id;
#MapsId("cartId")
#ManyToOne
#JoinColumn(name = "cart_id", referencedColumnName = "id", insertable = false, updatable = false)
private Cart cart;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "product_id", referencedColumnName = "id")
private Set<Product> product;
private int quantity;
private double price;
}
#Embeddable
public class CartDetailsId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "cart_id")
private Long cartId;
#Column(name = "product_id")
private Long productId;
}
How to avoid creation of this table (cart_details_product)? I think i don't need this table.
When a new address is added for a person, a new revision should be created. A revision is created, but the remaining fields of the entity in the revision are marked null.
Different and correct:
When I change a name for a person, a revision is created where all fields are entered.
Person Entity:
#Entity
#Table(name = "Person")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Audited
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator", sequenceName = "HIBERNATE_SEQUENCE", allocationSize = 1)
private Long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#OneToMany(mappedBy="person")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Address> addresses = new HashSet<>();
Person Audit Entity:
#Entity
#Table(name = "person_aud")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class PersonAud implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private AuditIdentity auditIdentity;
#Column(name = "revtype")
private Short revtype;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#ManyToOne
#MapsId("auditIdentity.id")
#JoinColumn(name = "id", nullable = false)
private Person person;
#OneToMany
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Address> addresses = new HashSet<>();
Address Entity:
#Entity
#Table(name = "address")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Audited
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator", sequenceName = "HIBERNATE_SEQUENCE", allocationSize = 1)
private Long id;
#Column(name = "street")
private String street;
#Column(name = "house_number")
private String houseNumber;
#Column(name = "zip_code")
private String zipCode;
#Column(name = "city")
private String city;
#Column(name = "state_province")
private String stateProvince;
#Column(name = "country")
private String country;
#ManyToOne
#JsonIgnoreProperties("addresses")
private Person person;
#OneToOne
#JsonIgnoreProperties("addresses")
#NotAudited
private PersonAud personAud;
If I add a new address that belongs to person XY, then my table looks like this:
PERSON_AUD:
ID: 1
REV: 1001
REVTYPE: 1
FIRST_NAME: NULL
LAST_NAME: NULL
For example, if I change the first name, the fields for the first_name and last_name are entered.
Problem solved (not perfect):
I changed the line:
#OneToMany(mappedBy="person")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Address> addresses = new HashSet<>();
to:
#OneToMany(cascade = {CascadeType.ALL})
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Address> addresses = new HashSet<>();
Now the process works like:
Add a new person with empty Address array
Add a new Address which refers to the person id
Action like a PUT on this Person assigning the address-object.
= PERSON_AUD table will contain the revision and the fields.
PROBLEM:
it is not perfect because you have to assign the address manually to the person.
Is there any other possible solution?
I am working on simple spring security demo and want to put user and role info into db. Here is the simple structure of my entity.
#Entity
#Table(name = "users")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Long userId;
#OneToMany(mappedBy = "users", fetch = FetchType.LAZY, orphanRemoval=true, cascade = CascadeType.ALL)
private List<UserRoleMapping> userRoleMapping;
}
//
#Entity
#Table(name = "user_role_mapping")
public class UserRoleMapping {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_role_mapping_id")
private Long userRoleMappingId;
#ManyToOne(fetch = FetchType.LAZY)
private Users users;
#ManyToOne(fetch = FetchType.LAZY)
private UserRole userRole;
}
//
#Entity
#Table(name = "users_role")
public class UserRole {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private Long roleId;
#Column(name = "role")
private String role;
#Column(name = "role_desc")
private String roleDesc;
#OneToMany(mappedBy = "userRole", fetch = FetchType.LAZY, orphanRemoval=true)
private List<UserRoleMapping> userRoleMapping;
}
In my use case, I have to make sure when the user is created then I have to insert subsequence userRoleMapping. And If the userRole is removed then the userRoleMapping must be deleted as well.
So I put CascadeType.ALL and orphanRemoval=true in users entity, and orphanRemoval=true in userRole entity.
However, when I run userRoleRepository.delete(userRole). I have dataIntegrityViolationException.
I did some researches on it and understand it is a kind of jpa constrains to make sure we delete the parents (users) as well.
May I ask if there are any workaround for my use case?
Thanks
Here is the solution to my use cases. Instead of using OneToMany in both entity, I should use ManyToMany relationship. And it is quite make sense as I don't care (for now) the mapping in java logic. Here is my code
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Long userId;
#ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.PERSIST })
#JoinTable
private Set<UserRole> userRole;
}
//
public class UserRole {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private Long roleId;
#Column(name = "role")
private String role;
#Column(name = "role_desc")
private String roleDesc;
#OneToMany(mappedBy = "userRole", fetch = FetchType.LAZY)
private Set<Users> users;
}
What I'm trying to do is to load only promotions with promotion.enabled=1 AND PromotionType.enabled=1.
I tried to add the #Where annotation in both tables but when I set promotionType, enabled to 0 I´m getting an error.
On the other hand, I also tried to add the #WhereJoinTable clause to promotionType but I'm not getting the expected result. Any help?
The first one:
#Entity
#Table(name = "HOTEL_PROMOTION")
#Where(clause = "enabled=1")
public class Promotion implements Serializable {
private static final long serialVersionUID = 257070400893576505L;
#Id
#Column(name = "PROMOTION_ID")
private Long id;
#Column(name = "CODE")
private String code;
#Column(name = "NAME")
private String name;
#Column(name = "PRIORITY")
private Long priority;
#ManyToOne
#JoinColumn(name = "PROMOTION_TYPE_ID")
#WhereJoinTable(clause = "enabled=1")
private PromotionType promotionType;
#Column(name = "ENABLED")
private Boolean enabled;
}
The second one:
#Entity
#Table(name = "HOTEL_PROMOTION_TYPE")
public class PromotionType implements Serializable {
private static final long serialVersionUID = -8359165117733458987L;
#Id
#Column(name = "PROMOTION_TYPE_ID")
private Long id;
#Column(name = "CODE")
private String code;
#Column(name = "NAME")
private String name;
#Column(name = "STYLE")
private String style;
#Column(name = "ENABLED")
private Boolean enabled;
}
I have three tables each mapping to one of these entities. The 'assigned' table acts as the relationship between 'users' and 'roles' with a foreign key to each table. How would I map this on my entities so that I can get a Set of EntityRoles from the UserEntity? I can't quite figure out how to make this work. Is this even possible?
#Entity
#Table(name = "users")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="user_id")
private long id;
#Column(name="user_username")
private String username;
#Column(name="user_password")
private String password;
#Column(name="user_email")
private String email;
//I want to be able to get a set of RoleEntities
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id")
private Set<RoleEntity> roles;
}
#Entity
#Table(name = "assigned")
public class AssignedEntity implements Serializable {
#Id
//#Column(name = "assigned_role")
#ManyToOne(targetEntity = RoleEntity.class, fetch = FetchType.LAZY)
#JoinColumn(name = "fk_role")
private long roleId;
#Id
//#Column(name = "assigned_user")
#ManyToOne(targetEntity = UserEntity.class, fetch = FetchType.LAZY)
#JoinColumn(name = "fk_user")
private long userId;
}
#Entity
#Table(name = "roles")
public class RoleEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="role_id")
#OneToOne(fetch = FetchType.LAZY, mappedBy="roleId")
private long id;
#Column(name="role_name")
private String name;
}
You are using an incorrect/inconvenient mapping. Always keep things as simply as possible.
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
}
#Entity
#Table(name = "roles")
public class Role {
#Id
private Long id;
#Column
private String name;
}
A persistent provider will create a (valid) join table for you. You can specify the name of the join table using #JoinTable annotation. Also you will need to think about auto generation values of id for the Role entity: the roles table is something like a reference data table. So, probably, you will need to hardcode the id values.
To get user roles (in the persistent context):
user.getRoles()