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.
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 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.
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!
I’m using spring data and hibernate as JPA implementation with spring boot. I’m new to this.
I have an entity where I’m maintaining the mapping between two entities which already exist and entries into this mapping only come in at the time of a specific update.
#Entity
#Table(name = "song_genres")
public class SongGenres {
public SongGenres(Song song, List<Genre> genres) {
this.genres = genres;
this.song = song;
}
public SongGenres() {
// Empty constructor as required by hibernate
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "song_id")
private Song song;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "genre_id")
private List<Genre> genres;
public List<Genre> getGenres() {
return genres;
}
}
I’m trying to insert all the genre values associated with a song at once, using
SongGenres songGenres = new SongGenres(song, genres);
songGenresRepository.save(songGenres);
but this is giving an error that
java.sql.SQLException: Field 'genre_id' doesn't have a default value
and the sql logs show a single row insert
Hibernate: insert into song_genres (song_id) values (?)
How is multiple row insert in one-to-many achieved without cascade?
For now, I changed the entity definition since there isn't much difference between the two
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "chapter_id")
private Chapter chapter;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "genre_id", nullable = false)
private Genre genre;
and the save operation becomes
List<ChapterGenre> chapterGenres = new ArrayList<>();
for (Genre genre : genres) {
chapterGenres.add(new ChapterGenre(chapter, genre));
}
chapterGenreRepository.save(chapterGenres);
From this one, concluded there isn't much of a difference from spring's implementation point of view.
Although this ain't best performance mysql-wise. Would be interesting if spring data comes up with a simple single insert API.
Just define CASCADE type for your List here "List genres"
Add No of Items in list and persist main entity.
You can read more about it here
Eg:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "visitId")
private Collection<SampleData> lstSampleData;
I' using Hibernate 5 with PostgreSQL. All is fine but I've a problem with the #Formula annotation.
I've this entity
public class MyImage implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Lob
private byte[] content;
private Boolean favorite;
#ManyToOne
#JoinColumn(name = "father_id", nullable = false, updatable = true, insertable = true)
private Father father;
and this
public class Father implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true,mappedBy = "father")
private Set<MyImage> images;
#Formula("(select ai.content from my_image ai where ai.father_id = id and ai.favorite = 1)")
private byte[] neededBytes;
With MySQL is working fine, but with PostgreSQL I've some SQL Grammar Exception. Testing the #Formula's query inside the SQL editor, seems to be that for PostgreSQL istead of use
ai.favorite = 1
I need to use
ai.favorite = TRUE
but even if I change this query, I always get an SQL Grammar Exception.
Can you help me?