Spring boot / Hibernate problems with #UniqueConstraint - java

I have Spring boot 1.4.3 + Hibernate 5.0.11 + H2 database.
My application startup fails when I try to use #UniqueConstraint with certain values. Here is it. Response class is marked with #UniqueConstraint with columnNames "survey" and "user"
#Entity
#Table(name = "responses", uniqueConstraints = {#UniqueConstraint(columnNames = {"survey, user"}, name = "responses_survey_user_idx")})
public class Response extends BaseEntity
{
#NotNull
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "survey", nullable = false)
private Survey survey;
#NotNull
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "user", nullable = false)
private User user;
#NotNull
private String answers;// json serialized
}
Survey class:
#Entity
#Table(name = "surveys", uniqueConstraints = {#UniqueConstraint(columnNames = {"name"}, name = "surveys_name_idx")})
public class Survey extends BaseEntity{
#NotNull
#SafeHtml
#Length(min = 3)
private String name;
#NotNull
#SafeHtml
#Length(min = 3)
private String description;
#OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "survey")
private List<Response> responses;
}
User class:
#Entity
#Table(name = "users", uniqueConstraints = {#UniqueConstraint(columnNames = "login", name = "users_unique_email_idx")})
public class User extends BaseEntity {
#NotEmpty
#SafeHtml
private String login;
#NotEmpty
#Length(min = 8)
#SafeHtml
private String password;
#OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "user")
private List<Response> responses;
}
The error I get is:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Unable to create unique key constraint (survey, user) on table responses: database column 'survey, user' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
I've tried to change names of columns in Response class, but it didn't help.
It work only when I remove #UniqueConstraint in this class at all .
Any ideas how to deal with this?

The column names in your unique constraint should not be comma separated but rather separate String values in an array (see http://docs.oracle.com/javaee/6/api/javax/persistence/UniqueConstraint.html):
#Table(
name="EMPLOYEE",
uniqueConstraints=
#UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})
)

Related

I am unable to create entity or fetch list of entities due to stack overflow error on bi-directional #ManyToMany relationship SpringDataJPA

I am working on a springboot application. I have 2 entity classes, Group and User. I also have #ManyToMany relationship defined in the Group class (Owning entity), and also in the User class, so that I can fetch all the groups a user belongs to. Unfortunately, I can't create a new group or a new user due to the following error;
{
"timestamp": "2022-09-09T20:29:22.606+00:00",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/json;charset=UTF-8' not supported"
}
When I try to fetch all groups a user belongs to by calling user.get().getGroups(); I get a a stack overflow error
Note: Currently I have #JsonManagedReference and #JsonBackReference in Group and User classes respectively. I also tried adding #JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") on both classes, but this did not work either. Adding value parameter to #JsonManagedReference and #JsonBackReference as demonstrated below did not work either. What am I doing wrong? What am I missing?
This is my Group entity class
#Table(name = "`group`") // <- group is a reserved keyword in SQL
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonView(Views.Public.class)
private String name;
private Integer maximumMembers;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinTable(name = "group_user", joinColumns = #JoinColumn(name = "group_id"), inverseJoinColumns = #JoinColumn(name = "user_id"))
#JsonView(Views.Public.class)
#JsonManagedReference(value = "group-member")
private Set<User> groupMembers;
}
This is my User entity class
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(Views.Public.class)
private Long id;
#JsonView(Views.Public.class)
private String nickname;
#JsonView(Views.Public.class)
private String username; // <- Unique user's phone number
private String password;
#ElementCollection(targetClass = ApplicationUserRole.class)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
#Column(name = "role")
private Set<ApplicationUserRole> roles;
#ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
#JsonBackReference(value = "user-group")
private Set<Group> groups;
}
Minimal, Reproducible Example https://github.com/Java-Techie-jt/JPA-ManyToMany
I found a permanent solution for this problem. For anyone else facing a similar problem, This is what I found. First, my entity classes had #Data Lombok annotation. I removed this because the #Data annotation has a tendency of almost always loading collections even if you have FetchType.LAZY.
You can read more about why you should't annotate your entity class with #Data here https://www.jpa-buddy.com/blog/lombok-and-jpa-what-may-go-wrong/
After removing this annotation, I removed #JsonManagedReference and #JsonBackReference from both sides of the relationship(both entities). I then added #Jsonignore to the referencing side only(User class). This solves 2 things
Creating a group with a list of users works fine
Adding a list of users to a group works fine.
After this, we are left with one last problem. When we try to read a user from the api, we get a user without the associated list of groups they belong to, because we have #JsonIgnore on the user list. To solve this, I made the controller return a new object. So after fetching the user from my service, I map it to a new data transfer object, the I return this object in the controller.
From here I used #JsonView to filter my responses.
This is how my classes look, notice there is no #Data in annotations.
Group
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Getter
#Setter
#Table(name = "`group`") // <- group is a reserved keyword in SQL
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private Integer maximumMembers;
#ManyToMany(fetch = FetchType.EAGER,
cascade = {CascadeType.MERGE, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinTable(name = "group_user",
joinColumns = #JoinColumn(name = "group_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
#JsonView(UserViews.PublicUserDetails.class)
private Set<User> groupMembers = new HashSet<>();
}
User
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(UserViews.PublicUserDetails.class)
private Long id;
#JsonView(UserViews.PublicUserDetails.class)
private String nickname;
#JsonView(UserViews.PublicUserDetails.class)
private String username; // <- Unique user's phone number
private String password;
#ElementCollection(targetClass = ApplicationUserRole.class)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
#Column(name = "role")
#JsonView(UserViews.PublicUserDetails.class)
private Set<ApplicationUserRole> roles;
#JsonIgnore
#ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
private Set<Group> groups = new HashSet<>();
}
Method fetching user in user controller
#GetMapping("/get-groups")
public ResponseEntity<UserRequestResponseDTO> getWithGroups(#RequestParam(name = "userId") Long userId) {
User user = userService.getWithGroups(userId);
UserRequestResponseDTO response = UserRequestResponseDTO.builder()
.nickname(user.getNickname())
.username(user.getUsername())
.groups(user.getGroups())
.build();
return ResponseEntity.ok().body(response);
}
Hopefully this helps someone💁

Many to Many Spring MVC mappedBy reference an unknown target entity property

Good day everyone
I’m trying to create a relationship for the entities Shelter and Owner, many to many, but a mistake is climbing, I do not understand what's the matter
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#DynamicUpdate
#Entity
#Table(name = "owner")
public class Owner {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idOwner;
private String name;
private String address;
private String description;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(
name = "owner_shelter",
joinColumns = {#JoinColumn(name = "owner")},
inverseJoinColumns = {#JoinColumn(name = "shelter")}
)
private Set<Shelter> shelterOwner;
}
--
#Data
#DynamicUpdate
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Entity
#Table(name = "shelter")
public class Shelter {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String address;
private String description;
#ManyToMany(mappedBy = "shelter")
private Set<Owner> sheltersOwner;
}
and the error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: ru.itis.springbootdemo.models.Owner.shelter in ru.itis.springbootdemo.models.Shelter.sheltersOwner
Error message is explicit , this is not correct
#ManyToMany(mappedBy = "shelter")
private Set<Owner> sheltersOwner;
should be
#ManyToMany(mappedBy = "shelterOwner")
private Set<Owner> sheltersOwner;
mappedBy references the other side attribute name and in your code it is not correctly set.
problem is field name of the shelter in Owner class, it must be private Set<Shelter> shelter;
not private Set<Shelter> shelterOwner;

org.springframework.beans.factory.BeanCreationException: in Ecommerce project

I work with a Spring boot project and when I run, I get the following error,
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userSecurityService': Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Could not create query metamodel for method public abstract Ecommerce.entities.User Ecommerce.repository.UserRepository.findByUsername(java.lang.String)!
I assume the last lines are what is important in the error stack,
Could not create query metamodel for method public abstract Ecommerce.entities.User Ecommerce.repository.UserRepository.findByUsername(java.lang.String)!
I have the User repository provided below,
public interface UserRepository extends CrudRepository<User, Long> {
User findByUsername(String username);
User findByEmail(String email);
}
The user entity is also provided,
#Entity
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "userId", nullable = false, updatable = false)
private Long userId;
private String userName;
private String password;
private String firstName;
private String lastName;
#Column(name = "email", nullable = false, updatable = false)
private String email;
private String phone;
private boolean enabled = true;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
private ShoppingCart shoppingCart;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserShipping> userShippingList;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserPayment> userPaymentList;
#OneToMany(mappedBy = "user")
private List<Order> orderList;
#JsonIgnore
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<UserRole> userRoles = new HashSet<>();
// ..............
// more lines of code for overriding the methods
}
What is the issue here and how to solve it?
In Spring JPA Repository, the auto-generated finders obey the naming convention as follows.
findBy<DataMember><Op>
<Op> can be Like,Between etc..
Apparently, the name of the method findByUsername should match with the property in the user entity. In the user entity class, I have used,
#Entity
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "userId", nullable = false, updatable = false)
private Long userId;
private String userName;
}
After I changed to the username from the userName, the problem is solved. I have done this after consulting other SOF posts, but, I still seek for the better explanation.

com.microsoft.sqlserver.jdbc.SQLServerException: Invalid column name 'xxx'

Entity class 1
#Entity
#Table(name = "TICKETS")
public class Ticket {
....
#Column(name = "MERCHANT_NBR")
private String merchant_nbr;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "merchant_nbr", nullable = false)
private Merchant merchant;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name ="merchantNBR", nullable = false)
private merchantDetails merchantDetails;
Entity class 2
#Entity
#Table(name="MERCHANT_DETAILS")
public class merchantDetails {
#Id
#Column(name="MERCHANT_NBR")
private String merchantNBR;
#OneToMany(fetch = FetchType.LAZY)
private Set<Ticket> ticket;
error its giving...invalid column 'merchantNBR'.But I have a column by that name.
com.microsoft.sqlserver.jdbc.SQLServerException: Invalid column name 'merchantNBR'.
so it's a join column not a column and for that you have to use the proper annotation for joining two entities which is #JoinColumn
see this it may be helpful
#joinColumn(name = "MERCHANT_NBR" ,referencedColumnName="merchantNBR")
private String merchant_nbr;
here i supposed that you've changed the column name in entitie class 2 from
#Column(name="MERCHANT_NBR") to `#Column(name="merchantNBR")`

Hibernate database column not found - defined in annotation

I am getting an exception with hibernate that I can't figure out.
org.hibernate.AnnotationException: Unable to create unique key constraint (role, username) on table user_roles: database column 'role' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
I have defined the name of the column "role" with the annotation, am I missing something here?
Any help appreciated.
...
#Entity
#Table(name = "user_roles", uniqueConstraints = #UniqueConstraint(columnNames =
{ "role", "username" }) )
public class UserRole
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_role_id", unique = true, nullable = false)
private Integer userRoleId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "username", nullable = false)
private User user;
#Column(name = "role", nullable = false, length = 45)
private String role;
...
}

Categories

Resources