Why is this mapping unable to create the column id as autoincrement?
#Entity(name = "user_role")
public class UserRole {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false, unique = true)
private Long id;
#OneToOne(cascade = CascadeType.MERGE, targetEntity = Role.class)
#JoinColumn(name = "role_id")
private Role role;
#OneToOne(cascade = CascadeType.MERGE, targetEntity = User.class)
#JoinColumn(name = "user_id")
private User user;
}
Thanks in advance!
To use a AUTO_INCREMENT column in your MySQL, you are supposed to use an IDENTITY strategy:
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id", nullable = false, unique = true)
private Long id;
//Your code
To learn more check this Link
It is clearly mentioned that
The IDENTITY strategy also generates an automatic value during commit for every new entity object.
Well, just to help people.
The problem was the name of my entity "user_role", I don't know why but after changing the entity name to "userrole" everything worked and the id column was created as AUTOINCREMENT.
Hope this helps!
Related
I have created an Employee entity and a corresponding ProfilePicture entity:
#Entity
#Table(name = "employee")
public class Employee{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
}
#Entity
#Table(name = "profile_picture")
public class ProfilePicture {
#Id
private Long id;
#OneToOne
#MapsId
private Employee employee;
#Column(name = "image")
private byte[] image;
}
Now, I want to add a default image to my profile_picture table. I am using a MySQL database and introducing a default image seems to be a problem, since my profile_picture database entries require an association with an employee. The issue is that MapsId is trying to map the id of the associated employee to the id of my profile-picture. When there is no associated employee, this is impossible.
Does anyone have an idea how to solve this problem? I know I could create a dummy employee, but I don't want to do this. I would like to have an entry in my profile_picture table for which the employee_id column is simply null. Right now, however, this is violating some constraints. I know I will have to think of a different solution, I am just asking for ideas.
I have three entity include bridge entity:
Team Entity:
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "TEAM_ID")
private Integer id;
#Column(name = "teamname", length = 128, nullable = false, unique = true)
private String teamname;
#Column(name = "delete_date", length = 128, nullable = true)
private Date delete_date;
#Column(name = "description", nullable = true, length = 240)
private String description;
#Column(name = "active", length = 64, nullable = false)
private int active;
#OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
---getter setter constructur
}
User Entity:
#Entity
#Table(name = "tblUsers")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "Username", length = 128, nullable = false, unique = true)
private String username;
#Column(name = "FirstName", nullable = false, length = 45)
private String firstName;
#Column(name = "LastName", nullable = false, length = 45)
private String lastName;
#Column(name = "Password", length = 64, nullable = false)
private String password;
#Column(name = "Email", length = 128, nullable = false, unique = true)
private String email;
#Column(name = "Phone", length = 64, nullable = false, unique = true)
private String phoneNumber;
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
---getter setter constructur
}
TeamUsers - Bridge Entity with extra column(active):
#Entity
#Table(name = "team_users")
public class TeamUsers implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "TEAM_ID")
private Team team;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
#Column(name = "active")
private Integer active;
---getter setter constructur
}
In the Team repository I have code:
package com.crmbackend.allService.teamService.repo;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import com.crmbackend.entity.Team;
public interface TeamRepository extends PagingAndSortingRepository<Team, Integer> {
#Query("select t from Team t")
public List<Team> getAllTeamAndDetails();
}
If I call the getAllTeamAndDetails() method in Junit Test, the result is all team informations:
It basically tells me how many team I have, and team users object who belong to which team.
Now, my question is which I want to get all team information and team user information,
but only their active = 1 in the bridge table.
which means if Team User record has active = 0, then this user should not showing in the result.
How this query should be looks like or what is the best approach?
Thanks
This is not possible with the plain JPA/Hibernate or Spring Data JPA tools available. You have to use a DTO for this purpose. I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Team.class)
public interface TeamDto {
#IdMapping
Integer getId();
String getDescription();
#Mapping("team_users[active = 1].user")
Set<UserDto> getUsers();
#EntityView(User.class)
interface UserDto {
#IdMapping
Integer getId();
String getUsername();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TeamDto a = entityViewManager.find(entityManager, TeamDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<TeamDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
I am developing a spring boot project, linked to a MySQL database through Hibernate.
Everything runs fine when the database starts empty. However, when the dummy data data.sql file runs before lauching the application, and then inserting objects through the application, the insertion doesn't take into account previous IDs, therefore resulting in duplicate entries until the number of lines is reached :
I'll try explaining better with an example :
At application start, the data.sql file inserts 3 dummy users.
Reaching registration page to add a new user and submitting, Hibernate returns an error 'Duplicate entry '1' for PRIMARY key', then 'Duplicate entry '2' for PRIMARY key' and 'Duplicate entry '3' for PRIMARY key' after retrying two times.
At the fourth retry, the user is added.
The ID therefore does auto-increment, but doesn't take into account previously inserted rows through data.sql.
Note that I've tried playing around with the generation type (AUTO / IDENTITY) of the User class with no success, and my hibernate datasource is on create-drop mode.
Update :
User entity :
#Data
#Builder
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "users")
public class User {
public final static Role DEFAULT_ROLE = new Role();
static {
DEFAULT_ROLE.setId(2);
DEFAULT_ROLE.setRole("NORMAL");
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#Column(name = "user_name")
#Length(min = 5, message = "*Your user name must have at least 5 characters")
#NotEmpty(message = "*Please provide a user name")
private String userName;
#Column(name = "email")
#Email(message = "*Please provide a valid Email")
#NotEmpty(message = "*Please provide an email")
private String email;
#Column(name = "password")
#Length(min = 5, message = "*Your password must have at least 5 characters")
#NotEmpty(message = "*Please provide your password")
private String password;
#Column(name = "name")
#NotEmpty(message = "*Please provide your name")
private String name;
#Column(name = "last_name")
#NotEmpty(message = "*Please provide your last name")
private String lastName;
#Column(name = "active")
private Boolean active;
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
#Column(name = "phone_number")
#NotEmpty(message = "*Please provide a phone number")
private String phoneNumber;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address_id", referencedColumnName = "address_id")
private Address address;
}
Any idea what would solve it? :)
Cheers.
The problem is not in Hibernate, but in the statements in data.sql.
In those INSERT statements your probably give explicit values for the user_id column, hence MySQL does not increment its AUTO_INCREMENT counter. Hibernate's default generation strategy for MySQL is IDENTITY, which relies on the AUTO_INCREMENT feature.
The easiest solution is to omit the users_id column in the INSERT statements. You can however also set the initial AUTO_INCREMENT value for the table to a higher number:
ALTER TABLE users AUTO_INCREMENT=1001
Edit: Your identity field is declared as int id, therefore it can never be null. Change it to Integer, so that Hibernate can distinguish between persisted (id != null) and not persisted entities.
I have an entity in containing :
#Entity
#Table(name = "pictures")
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Getter
#Setter
public class PictureEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", insertable = false, updatable = false, nullable = false)
private UUID id;
#Column
private String path;
#Column(name = "thumb_path")
private String thumbPath;
#Column
#Enumerated(EnumType.STRING)
private Status status;
#Column(name = "creation_utc")
#Temporal(TemporalType.TIMESTAMP)
private Date creationTimeUtc;
#Column(name = "creation_local")
#Temporal(TemporalType.TIMESTAMP)
private Date creationTimeLocal;
#ManyToOne
#JoinColumn(name = "project_id", updatable = true, insertable = true)
private ProjectEntity project;
#ManyToOne
#JoinColumn(name = "user_id", updatable = true, insertable = true)
private UserEntity user;
#OneToOne(mappedBy = "picture", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private ProcessedPictureEntity processedPicture;
public enum Status {
VALIDATED,
PROCESSED,
REJECTED,
WAITING_VALIDATION
}
}
When I call a save with H2 database, it saves the "project_id" field too.
But if I use mysql, the generated query isn't the same, project is not saved (which I think is the correct behavior).
I want the test with H2 to crash if updatable/insertable on project_id are false.
How can I correct this ?
If I understand you correctly you have two problems:
H2 and MySQL behave differently causing bugs to slip through your tests.
You want to test if a certain field got updated.
For 1.: I recommend Testcontainers. It allows you to run tests with an actual MySQL database (or any other database that you can get a docker image for).
This will make your integration tests way more valuable.
For 2.: Execute whatever code you suspect does the update in question and then check if the field got changed.
Make sure the changes get flushed which is a common cause of tests not behaving as on things.
For checking for changes I recommend Springs JdbcTemplate for easily executing queries.
I am trying to write a RESTful API using Spring Boot and I am not able to figure out a way to map my relations in the database. I have a User and a Reports table. Each User can have multiple Reports, and a single report consists of "FROM USER" and "TO USER" columns to indicate who sent the report and to whom. My User ID is the primary key and for the Report table, I am generating REPORT ID as the primary key using AUTO INCREMENT. Here is my User model class -
#Entity
#Table (name = "user")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
private String password;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL)
private List<Report> reportReceivedList;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Report> reportSentList;
/* Getters and setters ..... */
}
Here is my Report Model class -
#Entity
#Table (name = "report")
#EntityListeners(AuditingEntityListener.class)
public class Report {
#Id
#Column (name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne()
#JoinColumn(name = "from_user_id")
private Long fromUserId; //THIS SHOULD BE FROM "USER" TABLE
#ManyToOne()
#JoinColumn(referencedColumnName = "to_user_id")
private Long toUserId; //THIS SHOULD BE FROM "USER" TABLE
#Temporal(TemporalType.DATE)
#CreatedDate
private Date createdAt;
private String observation;
private String context;
//Other variables and getters and setters .....
}
Can someone please show me a way to correctly define this relationship. My current model doesn't work. Also, I want rows from REPORT class to be deleted as soon as a user is deleted. Thanks!
I finally fixed it by changing my User class as follows -
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "to_user_id")
private List<Report> reportReceivedList;
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "from_user_id")
private List<Report> reportSentList;
And by changing my Report class as -
#Column(name = "from_user_id")
private Long fromUserId;
#Column(name = "to_user_id")
private Long toUserId;