How to define a field for loading multiple images in spring boot model - image

I am developing an application for vehicle stock tracking system using spring boot, angular and mysql. Multiple images of the vehicle will be loaded from the interface. Normally, when there is only one image, I define a field of type byte [] with #lob annotation. But how can I keep it in the database when more than one image comes in. I think a relational structure is required but I couldn't.
public class User extends BaseEntity{
#Column(name = "TC_NUM", unique = true)
#NotNull
private String tcNum;
#Column(name = "EMAIL", unique = true)
#NotNull
private String email;
#Column(name = "USERNAME", unique = true)
#NotNull
private String username;
#Column(name = "PASSWORD")
#NotNull
private String password;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_ROLES",
joinColumns = #JoinColumn(name = "USER_ID"), inverseJoinColumns = #JoinColumn(name = "ROLE_ID"))
#NotNull
private Set<Role> roles;
}

Yes, you need to have One to Many relation.
Add another db table and entity for vehicle images. Let's say we called it VehicleImage:
#Entity
#Table(name="vehicle_image")
public class VehicleImage{
#Id
private Long id;
#Lob
#Column(name = "image", columnDefinition="BLOB")
private byte[] image;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
public VehicleImage() {}
// getters and setters
}
And add mapping to your User class like that:
public class User extends BaseEntity{
#OneToMany(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<VehicleImage> vehicleImages;
public void addVehicleImage(VehicleImage vehicleImage) {
vehicleImages.add(vehicleImage);
vehicleImage.setUser(this);
}
public void removeVehicleImage(VehicleImage vehicleImage) {
vehicleImages.remove(vehicleImage);
vehicleImage.setUser(null);
}
//rest of your class
}
As you can see I've also added two utility methods to User class. For details, see this great post by Vlad Mihalcea -> https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
Now you can persist your images like that:
User user = new User();
user.addVehicleImage(
new VehicleImage (imageBytesArray)
);
entityManager.persist(user);

Related

JPA onetomany mapping showing nested data many times

I have two table user(id,name) and user_mails(id,email,user_id) user to user_mails have one to many relation.
I have created following entity in spring boot
User
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "name", nullable = false)
private String name;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private Set<UserMail> userMails =new HashSet<UserMail>(0);
//Getter setter and constructor
}
UserMail
#Entity
#Table(name = "user_mails")
public class UserMail {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "email", nullable = false)
private String name;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id")
private User user;
It is showing following output on calling controller
[{"id":1,"name":"Ram","userMails":[{"id":2,"name":"ram#b.com","user":{"id":1,"name":"Ram","userMails":[{"id":2,"name":"ram#b.com","user":{"id":1,"name":"Ram","userMails":[{"id":2,"name":"ram#b.com","user":{"id":1,"name":"Ram","userMails":[{"id":2,"name":"ram#b.com","user":{"id":1,"name":"Ram","userMails":[{"id":2,"name":"ram#b.com","user":{"id":1,"name":"Ram","userMails":[{"id":2,"name":"ram#b.com","user":{"id":1,"name":"Ram","userMails":
and more
I want to access all users with all mail ids also want to acces mail id with user details
What changes should I do to get proper result

How can I retrieve all the children of a record in this Hibernate #ManyToOne relation?

I am working on a Spring Boot project using Spring Data JPA and Hibernate mapping. I have the following doubt about how can I implement the following query.
I have an User entity class like this:
#Entity
#Table(name = "portal_user")
#Getter
#Setter
public class User implements Serializable {
private static final long serialVersionUID = 5062673109048808267L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "first_name")
#NotNull(message = "{NotNull.User.firstName.Validation}")
private String firstName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "surname")
#NotNull(message = "{NotNull.User.surname.Validation}")
private String surname;
#Column(name = "sex")
#NotNull(message = "{NotNull.User.sex.Validation}")
private char sex;
#Column(name = "birthdate")
#NotNull(message = "{NotNull.User.birthdate.Validation}")
private Date birthdate;
#Column(name = "tax_code")
#NotNull(message = "{NotNull.User.taxCode.Validation}")
private String taxCode;
#Column(name = "e_mail")
#NotNull(message = "{NotNull.User.email.Validation}")
private String email;
#Column(name = "pswd")
#NotNull(message = "{NotNull.User.pswd.Validation}")
private String pswd;
#Column(name = "contact_number")
#NotNull(message = "{NotNull.User.contactNumber.Validation}")
private String contactNumber;
#Temporal(TemporalType.DATE)
#Column(name = "created_at")
private Date createdAt;
#Column(name = "is_active")
private boolean is_active;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference
private Set<Address> addressesList = new HashSet<>();
#ManyToMany(cascade = { CascadeType.MERGE })
#JoinTable(
name = "portal_user_user_type",
joinColumns = { #JoinColumn(name = "portal_user_id_fk") },
inverseJoinColumns = { #JoinColumn(name = "user_type_id_fk") }
)
Set<UserType> userTypes;
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
public User() {
super();
}
public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
String email, String pswd, String contactNumber, Date createdAt, boolean is_active) {
super();
this.firstName = firstName;
this.middleName = middleName;
this.surname = surname;
this.sex = sex;
this.birthdate = birthdate;
this.taxCode = taxCode;
this.email = email;
this.pswd = pswd;
this.contactNumber = contactNumber;
this.createdAt = createdAt;
this.is_active = is_active;
}
}
The instances of this class represents users of my system. An user can have a single specific parent (the concept is similar to that of a referral: an user can bring another user in the system). This is handled by this ManyToOne recursive relationship:
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
Basically an user contains is parent (who bring him\her into the platform). It works fine. So retrieving an user I can easily retrieve the information of who is its parent (it is contained into the retrieved User object).
Now I need to implement the inverse behavior: I have to define a "query" that starting from a parent retrieve all its children.
The previous User entity class maps the following DB table:
The highlighter parent_id contains the FK that define this recursive relationship. So it contains the PK of another user that is the parent.
I have this UserRepository repository interface (it extents the JpaRepository interface)
public interface UsersRepository extends JpaRepository<User, Integer> {
User findByemail(String email);
List<User> findByUserTypes_TypeName(String typeName);
}
As you can see I am using a "query by method" style. Is it possiblem implement a behavior like this using "query by method" style? (in case also JPQL could be fine)
You can do this
List<User> findByParent_Id(Integer id);
Or you can do this
#Query("SELECT u FROM User u WHERE u.id = ?1")
List<User> getReferredUsers(Integer id);
The relationship between the user and the parent is unidirectional in the given code. By making it bidirectional, it is easy to query the data in either ways.
Refer to below code to make it bidirectional. Also ensure the relevant FetchType to avoid the performance risk. Here FetchType.LAZY is used for one to many association so it queries the data using the proxy reference when needed.
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
#JsonBackReference
private User parent;
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
private Set<User> userSet = new HashSet<>();
Child entities are fetched only when parent.getUserSet is used because of the FetchType.Lazy
public Set<User> getUsers(int id) {
User parent = userRepository.getById(id);
return parent.getUserSet();
}

JpaRepository returning child for the first item in the list and then only the id for the rest

I have the following Post class:
#Entity
#Table(name = "posts")
#Getter
#Setter
#JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Long.class)
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String subtitle;
private String content;
private String img_url;
#CreationTimestamp
private Timestamp created_on;
#UpdateTimestamp
private Timestamp last_updated_on;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "owner_id", nullable=false)
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private User creator;
}
And the following repository that extends JpaRepository
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
Optional<Post> findById(Long id);
List<Post> findAll();
}
When returning the result of findAll() inside the following controller, only the first creator item is sent completely and the rest just contain the id:
#GetMapping("/news")
public List<Post> getNews() {
return postRepository.findAll();
}
This is the JSON I get as result:
[
{"id":15,"title":"Title example #1","subtitle":"Subtitle example #1","content":"Lorem #1 ipsum dolor sit amet","img_url":null,"created_on":"2021-12-01T00:00:00.000+00:00","last_updated_on":"2021-12-01T00:00:00.000+00:00","creator":{"id":1,"username":"user-example","email":"blablabla#gmail.com","roles":[{"id":1,"name":"ROLE_USER"}]}}
,{"id":25,"title":"Title example #2","subtitle":"Subtitle example #2","content":"Lorem #2 ipsum dolor sit amet","img_url":null,"created_on":"2021-12-01T00:00:00.000+00:00","last_updated_on":"2021-12-01T00:00:00.000+00:00","creator":1}
]
Why is this happening? Is there a way I can get the whole child object for every element in the JSON array?
Thanks
EDIT: added the User class
#Entity
#Table( name = "users",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username"),
#UniqueConstraint(columnNames = "email")
})
#DiscriminatorValue(value="USER")
public class User extends OwnerEntity {
#NotBlank
#NotNull
#Size(max = 20)
private String username;
#NotBlank
#NotNull
#Size(max = 50)
#Email
private String email;
#NotBlank
#Size(max = 120)
#JsonIgnore
private String password;
#CreationTimestamp
private Timestamp created_on;
#UpdateTimestamp
private Timestamp last_updated_on;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "user_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY)
private Set<Institution> institutions;
#OneToMany(mappedBy="creator", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
protected Set<Post> posts;
#ManyToMany(fetch = FetchType.LAZY)
private Set<Institution> following;
}
EDIT 2: Added the OwnerEntity class
#Entity
#Table(name = "entities")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn
#Getter
#Setter
#JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Long.class)
public class OwnerEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
}
Your OwnerEntity also has #JsonIdentityInfo. In its reference documentation we can read the following:
Annotation used for indicating that values of annotated type or
property should be serializing so that instances either contain
additional object identifier (in addition actual object properties),
or as a reference that consists of an object id that refers to a full
serialization. In practice this is done by serializing the first
instance as full object and object identity, and other references to
the object as reference values.
This perfectly explains why you are getting the JSON like that. If you don't want this, just remove #JsonIdentityInfo but it might be there to fix an infinite recursion while serializing bidirectional relationships (you can read more about this in the following online resource https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion).

JPA and Hibernate One To One Shared Primary Key Uni-directional Mapping in Spring Boot

I want to have one-to-one uni-directional mapping with 2 child entities using shared primary key. Below are model classes
public class Template implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "templatekey")
Integer templateKey;
#Column(name = "templateid", unique = true)
String templateId;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#PrimaryKeyJoinColumn(name = "templatekey", referencedColumnName = "templatekey")
InstantOfferNoEsp instantOfferNoEsp;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#PrimaryKeyJoinColumn(name = "templatekey", referencedColumnName = "templatekey")
Mobile mobile;
//constructor , setter and getters
}
Child 1 :
public class Mobile implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "templatekey")
Integer templateKey;
String mobileNewUrl;
//constructor , setter and getters
}
Child 2:
public class InstantOfferNoEsp {
#Id
#Column(name = "templatekey")
Integer templateKey;
String offerCodeType;
String headerUrl;
//constructor , setter and getters
}
I want templateKey as PK in all tables. and I am calling templateRepository.save(template); to save all entities at once but its not working and getting ids for this class must be manually assigned before calling save() error.
Any suggestions would be of great help. Thank you.
I was able to do what you want with bidirectional #OneToOne like below:
#Entity
public class Mobile {
#Id
Integer templateKey;
#OneToOne
#MapsId
#JoinColumn(name = "templatekey")
Template template;
// ...
}
#Entity
public class InstantOfferNoEsp {
#Id
Integer templateKey;
#OneToOne
#MapsId
#JoinColumn(name = "templatekey")
Template template;
// ...
}
#Entity
public class Template {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "templatekey")
Integer templateKey;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "template", optional = false)
InstantOfferNoEsp instantOfferNoEsp;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "template", optional = false)
Mobile mobile;
// ...
public void setMobile(Mobile mobile)
{
this.mobile = mobile;
this.mobile.setTemplate(this);
}
public void setInstantOfferNoEsp(InstantOfferNoEsp instantOfferNoEsp)
{
this.instantOfferNoEsp = instantOfferNoEsp;
this.instantOfferNoEsp.setTemplate(this);
}
}
and an example of saving:
Mobile mobile = new Mobile();
mobile.setMobileNewUrl("MOB1");
InstantOfferNoEsp instant = new InstantOfferNoEsp();
instant.setOfferCodeType("INST_OFF1");
Template template = new Template();
template.setTemplateId("TMP1");
template.setInstantOffer(instant);
template.setMobile(mobile);
entityManager.persist(template);
P.S. The following mapping works too, but only if we set Template.templateKey manually.
#Entity
public class Template
{
#Id
// #GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "templatekey")
Integer templateKey;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "templatekey", insertable = false, updatable = false)
InstantOfferNoEsp instantOfferNoEsp;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "templatekey", insertable = false, updatable = false)
Mobile mobile;
// ...
}
and an example of saving:
Mobile mobile = new Mobile();
mobile.setMobileNewUrl("MOB1");
InstantOfferNoEsp instant = new InstantOfferNoEsp();
instant.setOfferCodeType("INST_OFF1");
Template template = new Template();
template.setTemplateKey(20);
template.setTemplateId("TMP1");
template.setInstantOffer(instant);
template.setMobile(mobile);
entityManager.persist(template);
Also I would suggest your explicitly specify what generation strategy you want to use (do not use GenerationType.AUTO) and use corresponding object wrapper classes instead of primitive types for #Id fields.

How should i define which Hibernate mapping to use, and when/where to use it?

i and having a use case where there are 2 kinds of users namely the "clients" and "professionals". These 2 entities have a parent entity called the "users" where each "user" has one entry in either "client/professional" depending on their role.
Let's consider a "clients".
A "user" has a one-to-one mapping with a "client"
A client might have several "companies" under him, i.e "client" has one-to-many relationship with "companies".
I am creating a REST API for this use case using spring boot. I still dont have any idea about why i should be using mapping in Hibernate. So far the only advantage i see is that, the CASCADING property of it. If a "user" gets removed, all the tables having the "user-id" will also be flushed. But consider a scenario where i need to add "companies" for a "client". I am confused to whether i should persist "companies" via "clients" entity or should i directly persist to "client" entity. I dont see any major advantage here because in both cases we are checking whether a "client" exists with the given ID before persisting in the "clients" table.
User Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long UID;
private Integer userRoleId;
private String username;
private String email;
private String phoneNumber;
private String firstName;
private String lastName;
private Long dateOfJoin;
private Boolean activeStatus;
private Long createdAt;
private Long updatedAt;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
private Client client;
}
Client Entity
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long CID;
#Column(unique = true)
private Long userId;
private Long createdAt;
private Long updatedAt;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "client")
private ClientCompany clientCompany;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "userId" ,referencedColumnName = "UID", insertable = false, updatable = false)
private User user;
}
Client Company Entity
public class ClientCompany {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long CCID;
private Long clientId;
private String email;
private String phoneNumber;
public String streetAddress1;
public String streetAddress2;
public String zipCode;
public String city;
public String state;
public String country;
private Long createdAt;
private Long updatedAt;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "clientId", referencedColumnName = "CID", insertable = false, updatable = false)
private Client client;
}
The advantage of using Hibernate/JPA is that you do not need to code JDBC calls.
You just use objects.
In your scenario,
load a Client instance from the database;
create a ClientCompany object;
assign the Client instance to it (no need to check the client existence since you loaded it from the database);
save to database.
Hibernate will take care of everything without you writing any SQL statements.
Step 1) can also be replaced with creating a new Client that will be saved to the database, but again Hibernate will handle saving correctly (if you configured it correctly).

Resources