I have a four entity class as listed bellow.
the mapping requirement is like-
1-Customer may have multiple address
2-Customer may have multiple and unique contact numbers.
3-Customer may have multiple and unique email-id's.
while running the application, i am getting the MultipleBagFetchException becoz of multi collection or might be bcoz of wrong mapping., can any one help me...
thanks
first
public class Customer{
#Id
#GeneratedValue(generator = "uuid")
#Access(AccessType.PROPERTY)
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Type(type = "uuid-char")
#Column(columnDefinition = "VARCHAR(36)", name = DatabaseConstants.ID)
private UUID id;
private String firstName;
private String middleName;
private String lastName;
private Date dob;
#OneToOne(cascade = CascadeType.ALL)
private Gender gender;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "customer")
#JoinColumn(name="department_id")
#Embedded
private List<Address> address;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "customer")
#Embedded
private Set<Email> email;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "customer")
#Embedded
private Set<Mobile> mobile;
}
Second-
public class Address {
#Id
#GeneratedValue(generator = "uuid")
#Access(AccessType.PROPERTY)
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Type(type = "uuid-char")
#Column(columnDefinition = "VARCHAR(36)", name = DatabaseConstants.ID)
private UUID id;
private String country;
private String state;
private String city;
private String streetAddress;
private String zip;
private String addressType;
#ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
private Customer customer;
}
Third-
public class Email {
#Id
#GeneratedValue(generator = "uuid")
#Access(AccessType.PROPERTY)
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Type(type = "uuid-char")
#Column(columnDefinition = "VARCHAR(36)", name = DatabaseConstants.ID)
private UUID id;
#javax.validation.constraints.Email
private String emailId;
#ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
private Customer customer;
private boolean primary;
}
Fourth-
public class Mobile {
#Id
#GeneratedValue(generator = "uuid")
#Access(AccessType.PROPERTY)
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Type(type = "uuid-char")
#Column(columnDefinition = "VARCHAR(36)", name = DatabaseConstants.ID)
private UUID id;
private String mobileNumber;
private boolean primary;
#ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
private Customer customer;
}
You do not need #Embedded attribute on these
private List<Address> address;
private Set<Email> email;
private Set<Mobile> mobile;
#Embedded is required only when you want to embed a composite value. Here you have a simple one to many relationship between entities. Remove the #Embedded on the class members and #Embeddable on child classes if they are present and put #Entity if not already present.
Related
I am working on a Spring Boot project using Spring Data JPA trying to adopt the "query by method name" style in order to define my queries into repositories.
I am finding some difficulties trying to implement a select query retrieving the list of objects based on two different "where condition". I will try to explain what I have to do.
First of all this is my main entity class named Wallet:
#Entity
#Table(name = "wallet")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Wallet implements Serializable {
private static final long serialVersionUID = 6956974379644960088L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "address")
private String address;
#Column(name = "notes")
private String notes;
#ManyToOne
#EqualsAndHashCode.Exclude // Needed by Lombock in "Many To One" relathionship to avoid error
#JoinColumn(name = "fk_user_id", referencedColumnName = "id")
#JsonBackReference(value = "user-wallets")
private User user;
#ManyToOne
#EqualsAndHashCode.Exclude // Needed by Lombock in "Many To One" relathionship to avoid error
#JoinColumn(name = "fk_coin_id", referencedColumnName = "id")
private Coin coin;
#ManyToOne
#JoinColumn(name = "type", referencedColumnName = "id")
private WalletType walletType;
public Wallet(String address, String notes, User user, Coin coin, WalletType walletType) {
super();
this.address = address;
this.notes = notes;
this.user = user;
this.coin = coin;
this.walletType = walletType;
}
}
As you can see a wallet is directly binded to a specific User object and to a specific Coin object.
For completeness this is the code of my User entity class:
#Entity
#Table(name = "portal_user")
#Getter
#Setter
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
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(value = "address")
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") }
)
private Set<UserType> userTypes;
#ManyToOne(fetch = FetchType.LAZY)
#JsonProperty("subagent")
private User parent;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference(value = "user-wallets")
private Set<Wallet> wallets = new HashSet<>();
public User() {
super();
// TODO Auto-generated constructor stub
}
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;
}
}
and this is the code of my Coin entity class:
#Entity
#Table(name = "coin")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Coin implements Serializable {
private static final long serialVersionUID = 6956974379644960088L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "name")
#NotNull(message = "{NotNull.Coin.name.Validation}")
private String name;
#Column(name = "description")
private String description;
#Column(name = "code", unique = true)
#NotNull(message = "{NotNull.Coin.code.Validation}")
private String code;
#Type(type="org.hibernate.type.BinaryType")
#Column(name = "logo")
private byte[] logo;
}
Then I have this WalletRepository interface:
public interface WalletRepository extends JpaRepository<Wallet, Integer> {
}
Here I need to define a query by name method that retrieve a specific wallet of a specific User (I think that I can query by the id field of the User) and based and related to a specific Coin (I think that I can query by the id fied of the Coin).
How can I implement a behavior like this?
The following should work:
public interface WalletRepository extends JpaRepository<Wallet, Integer> {
List<Wallet> findByUserIdAndCoinId();
}
You can read more about this at:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-keywords
Am doing one small activity of Teach and address relationship for one to many and in address block there will be one to one relationship between country, district, tahasil etc. Whenever am hitting api and to save it it's not updating or inserting Address in address table.
Detail is
#Entity
#Table(name = "teachers")
#PrimaryKeyJoinColumn(name = "user_id")
public class Teacher extends User {
#Size(min = 3, max = 50)
#Column(name = "first_name")
private String firstName;
#Size(min = 3, max = 50)
#Column(name = "middle_name")
private String middleName;
#Size(min = 3, max = 50)
#Column(name = "last_name")
private String lastName;
#OneToMany(fetch = FetchType.LAZY,mappedBy = "teacher")
#JsonManagedReference
private Set<Address> addresses = new HashSet<>(0);
Getter Setter...
}
Then Address Entity
#Entity
#Table(name = "address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "address_id")
private Long addressId;
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
#JsonBackReference
private Teacher teacher;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "country_id", referencedColumnName = "country_id")
private Country country;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "state_id", referencedColumnName = "state_id")
private State state;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "district_id", referencedColumnName = "district_id")
private District district;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "tahasil_id", referencedColumnName = "tahasil_id")
private Tahasil tahasil;
#Column(name = "line_one")
private String lineOne;
#Column(name = "line_two")
private String lineTwo;
#Column(name = "landmark")
private String landmark;
#Column(name = "pincode")
private Integer pincode;
public Country getCountry() {
return country;
}
Other Getter Setter
The Country example same to state, district and tahasil
#Entity
#Table(name = "countries", uniqueConstraints = { #UniqueConstraint(columnNames = { "country_name" }) })
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "country_id")
private Long countryId;
#NotBlank
#Size(min = 3, max = 50)
#Column(name = "country_name")
private String countryName;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "country")
private Address address;
Getter Setter
Finally in controller am doing like
Optional<Teacher> teacher = teacherRepo.findByUserId(id);
if (!teacher.isPresent())
return ResponseEntity.notFound().build();
teacher.get().setUserId(id);
teacher.get().setFirstName(teacherUpdateForm.getFirstName());
teacher.get().setMiddleName(teacherUpdateForm.getMiddleName());
teacher.get().setLastName(teacherUpdateForm.getLastName());
teacher.get().setAddresses(teacherUpdateForm.getAddresses());
userRepository.save(teacher.get());
Tried so may ways by referring multiple sites and readouts, but still not able to see any insert or update to address table. Please help me to get my mistake.
Regards,
Chetan
You need to cascade the persist of the Teacher entity.
Update the definition of the attribute Address inside the Teacher entity:
#OneToMany(fetch = FetchType.LAZY,mappedBy = "teacher", cascade = CascadeType.ALL)
#JsonManagedReference
private Set<Address> addresses = new HashSet();
You can play with the cascade type value as you want.
I generated application using Jhipster. In start everything was working fine but as application grow tournament entity become issue regarding performances.
This is my entity :
/**
* A Tournament.
*/
#Entity
#Table(name = "tournament")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "tournament")
public class Tournament implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "location")
private String location;
#Column(name = "url")
private String url;
#Column(name = "start_date")
private ZonedDateTime startDate;
#Column(name = "end_date")
private ZonedDateTime endDate;
#Column(name = "entry_fee")
private Double entryFee;
#Column(name = "prize")
private Double prize;
#Column(name = "goods")
private String goods;
#Column(name = "favorite_rating")
private Long favoriteRating;
#Column(name = "participants_number")
private Integer participantsNumber;
#Column(name = "finished")
private Boolean finished;
#Column(name = "view_only")
private Boolean viewOnly;
#Column(name = "image")
private String image;
#Column(name = "description")
private String description;
#Column(name = "teams_applied")
private String teamsApplied;
#Lob
#Column(name = "schedule")
private String schedule;
#Lob
#Column(name = "prize_distribution")
private String prizeDistribution;
#Lob
#Column(name = "contacts")
private String contacts;
#Lob
#Column(name = "rules")
private String rules;
#OneToMany(mappedBy = "tournament", fetch = FetchType.LAZY)
#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Stream> streams = new HashSet<>();
#ManyToMany
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "tournament_platforms", joinColumns = #JoinColumn(name = "tournaments_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "platforms_id", referencedColumnName = "id"))
private Set<Platform> platforms = new HashSet<>();
#ManyToMany(mappedBy = "favoriteTournaments", fetch = FetchType.LAZY)
#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<User> favoriteUsers = new HashSet<>();
#ManyToOne
private Game game;
#ManyToOne
private TournamentStatus status;
#ManyToOne
private EntryType entryType;
#ManyToOne
private TournamentFormat format;
#ManyToOne
private Region region;
#ManyToOne
private GameMode gameMode;
#ManyToOne
private PrizeType prizeType;
#ManyToOne
private Organizer organizer;
#ManyToOne
private TournamentStage stage;
#ManyToOne
private HostPlatform hostPlatforms;
#ManyToOne
private TournamentType type;
#ManyToOne
private PlayType playType;
#ManyToOne
private Currency currency;
#ManyToOne
private Country country;
I am using spring JPA. Getting 20 tournaments from database takes 39 seconds. That is not acceptable. Is there any way i can reduce it to normal speed. What is reason for such a long response time ? Every many to one relation i made unidire
In hibernate's implementation of JPA, #ManyToOne has a fetchType = EAGER by default and you have 14 of them.
#ManyToOne
private Country country;
That means 14 joins for each request. I highly recommend to use fetchType = LAZY for all relationships and deactivate them one by one when needed.
As a rule of thumb, you should not use more than 3 joins per request.
Also take a look at the generated request and use EXPLAIN PLAN in order to understand what the database really does and where it is costly. It will probably reveal some missing indexes on columns used as foreign keys...
I have some problems with identifier generation. I use MySQL database. So, I have two entities:
#Entity
#Table(name = "users", catalog = "test1")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id", unique=true, nullable=false, updatable=false)
private Long id;
private String username;
private String password;
private boolean enabled;
#JsonBackReference
private Set<UserRole> userRole = new HashSet<UserRole>(0);
#OneToOne(cascade={CascadeType.ALL}, fetch = FetchType.LAZY, targetEntity = Utilisateur.class)
#JoinColumn(name="userUtilisateur")
#JsonManagedReference
private Utilisateur userUtilisateur;
/*.. getters and setters..*/ }
and
#Entity
#Table(name="utilisateur")
public class Utilisateur {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
private String fullName;
#Column(name = "age")
private int age;
#OneToMany(mappedBy="utilisateur", targetEntity = Ticket.class, cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JsonBackReference
private Set<Ticket> tickets = new HashSet<Ticket>(0);
#OneToMany(mappedBy="utilisateur", targetEntity=UserAssignProject.class, cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JsonBackReference
private Set<UserAssignProject> userAssignProjects = new HashSet<UserAssignProject>(0);
#OneToMany(mappedBy="utilisateur", targetEntity=Message.class, cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JsonBackReference
private Set<Message> messages = new HashSet<Message>(0);
/*.. getters and setters..*/ }
I have this method in UserDaoImpl:
public void save(User user) {
Utilisateur utilisateur = new Utilisateur();
user.setId(user.getId());
user.setUsername(user.getUsername());
user.setPassword(user.getPassword());
user.setEnabled(true);
user.setUserUtilisateur(utilisateur);
getCurrentSession().save(user);
}
Results:
Exception here
I've tried to use sequence, GenerationType.AUTO..., but it's not working.
Any solutions?
Thanks for your attention!
What are some best practices when defining users with different roles/permissions, such as normal user with restricted access and administrator with full access?
My user class looks something like this:
#Entity
#Table(name = "users")
public class User {
#Id
#Column(name = "userID")
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
private Long userID;
public Long getUserID() {
return userID;
}
#ManyToOne(fetch = FetchType.LAZY)
#Column(nullable = false)
private Role role;
#Column(nullable = false)
private boolean isActive;
#Column(nullable = false, unique = true)
private String email;
#Column(nullable = false)
private String password;
#Column
#Temporal(TemporalType.TIMESTAMP)
private Calendar lastLoggedIn;
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Calendar createdDate;
#Version
private Integer version;
}
My Role class is:
#Entity
#Table(name = "roles")
public class Role {
#Id
#Column(name = "roleID")
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
private Long roleID;
#Column(nullable = false)
private String roleName;
#Column(nullable = false)
private String rolePermissions;
}
Say my application needed to retrieve a list of user and their roles, should an instance of User hold a reference to an instance (or proxy) of Role? What if my application had to find all Users for a particular Role, does Role have to have a List of Users? What are some tried and true ways of designing and implementing this relationship?
Also, am I doing the mapping correctly with JPA?
You should use,
#CollectionOfElements
#JoinTable(name = "ROLE_PERMISSIONS", joinColumns = #JoinColumn(name = "roleID"))
#Column(name = "Permission")
private List<String> rolePermissions;
instead of,
#Column(nullable = false)
private String rolePermissions;
I would suggest using
#role=Admin
for all the administrator level functions
and use
#role=User
for normal user and more if annotations if necesary