I am using Spring MVC, Hibernate to store 2 entities - FacilityMember and User. Business requirement is such that once entry is inserted into 'FacilityMember' then 'User' entry should get inserted with his email id as user name.
I have unidirectional mapping from FacilityMember to User. I need User->id for some further action inside my service layer. After hibernate persists I do get ID for FacilityMember but I get 0 for FacilityMember->User->Id.
Code snippet as follows:
My FacilityMember
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
//other attributes
#OneToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.SELECT)
#Cascade(CascadeType.SAVE_UPDATE)
#JoinColumn(name = "userId")
private User user;
//getters setters
My User entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 4, max = 30)
private String username;
//getters setters
My service layer is having method level transaction which calls below Dao layer code.
User user = new User();
user.setUsername(userName);
facilityMember.setUser(user);
persist(facilityMember);
return facilityMember;
Inside service layer my facilityMember->id is having proper value but
facilityMember->User->id is 0. Data gets stored properly into mysql tables.
Hibernate queries are executed in following manner
Insert FacilityMember ...
Insert User ..
Update FacilityMember ..
What's going wrong - how to get newly inserted id of my mapped entity ?
Thanks in advance
Manisha
Related
I have a user that can have a wallet. Now, when user create a wallet I want to give him a option to create a transaction on that wallet. So, on that form I would like to have next fields, so prompt user to insert:
Amount of transaction, Date, note of transaction, category of transaction
So far I have this in Spring:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "transaction_id")
private Integer id;
private double amount;
private String note;
#DateTimeFormat(pattern = "dd-MM-yyyy")
#Column(name = "date")
private Date date;
But I have problem with field category. I want to prompt user to pick from dropdown menu one of category from the list. But how to create a field categories that will be filled with names of categories?
I tried with:
#Column(name = "categories")
private List categories;
But I'm getting:
Could not determine type for: java.util.List, at table: transaction, for columns: [org.hibernate.mapping.Column(categories)]
Spring JPA lets you handle this a couple of different ways. Since you didn't specify what type of thing a category is, I am assuming you want it to be a String.
#ElementCollection
The first, easiest, and generally recommended way is to use the #ElementCollection. If your categories are fairly well fixed, you can even use enums for this.
#ElementCollection
private List<String> categories=new ArrayList<>();
With this, JPA will generate a second table in the database called transaction_categories You don't have to create an Entity for this table or anything. Everything is handled under the covers.
JsonB
If you are using postgres 10+ (I think) you can make a column into a JSON object. You will need the following dependency in your gradle.
implementation 'com.vladmihalcea:hibernate-types-52:2.15.1'
And you will need to change your model thus:
#TypeDefs({
#TypeDef(name = "json", typeClass = JsonType.class)
})
#Entity
public class Transaction {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "transaction_id")
private Integer id;
private double amount;
private String note;
#DateTimeFormat(pattern = "dd-MM-yyyy")
#Column(name = "date")
private Date date;
#Type(type = "json")
#Column(columnDefinition = "jsonb")
private List<String> categories=new ArrayList<>();
}
So long as this list does not become gigantic, it is a pretty decent solution.
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.
Say I have a class that holds user info
User.java
#Data
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
//User Data
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
}
In front end page admin can update Bio Dynamically by defining new field. Say clicking on + button he can add new field called middle name, age or address, etc.
P.S. It is kind of admin privilege and number of updates runtime will be limited and hence no issue of creating unlimited fields.
How can I address this dynamic addition of entity in MySQL using Spring Boot?
You might add new fields using a custom map, for example:
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "custom_fields")
#MapKeyColumn(name = "field")
private Map<String, String> customFields;
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;
For the relationship of many-to-one, one-to-many or even many-to-many, how to get an object without the objects included on the other side?
Say a group of address and a group of people, the relationship would be many-to-many, what if i just wanne get a "people" without the concerned address?
Classroom.java
#Entity
public class Classroom {
public Classroom(){
}
public Classroom(long id, String name) {
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "classroom_people",
joinColumns = {#JoinColumn(name = "classroom_id", referencedColumnName = "id") },
inverseJoinColumns = {#JoinColumn(name = "people_id", referencedColumnName = "id") })
private List<People> peoples = new ArrayList<>();
...
// getter() and setter()
}
People.java
#Entity
public class People{
public People(){
}
public People(long id, String name) {
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private long id;
#Column(name = "name")
private String name;
#ManyToMany(mappedBy = "peoples", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Classroom> classrooms = new ArrayList<>();
...
// getter() and setter()
}
By using hibernate, the table of Classroom, People and the join table classroom_people have been created automatically. Until now, everything is fine.
Then by using spring repository, I tried to fetch a people by passing the name, and return a JSON object.
What I hope to get is just a JSON including people.id and people.name but what I get is a JSON including one people and all the classroom in the Set, which includes all the concerning peoples.
SO I guess the problem is the List and List and what I should do is to create three tables: classroom, people and classroom_people, then store the relationship manually to join table. Then I can fetch a simple object without problem.
AM I right?
I use Spring Repository and findAllByName(), so the SQL is generated by spring. Just wonder whether this would be the source of problem.
It's easily done with HQL if you keep the relationships bidirectional.
Something like:
String hql = "select addr.people from " + Address.class.getName() + " addr where addr.name(or some other attribute) = :addressName"
//set parameter "addressName"
EDIT:
Now you took a different example, but the above query applies the same, for example getting people from a classroom.
About your last note, getting a People by name will fetch only People instance, because the #ManyToMany List referencing classrooms is LAZY which means is not populated until you call people.getClassRooms()(if you have the session open it will load the empty list with another query)
You don't need to create the junction table yourselves, not for this purpose.
How do you know that List<ClassRoom> is populated when you fetch a People? Because from your mappings it looks OK, unless you're calling people.getClassRooms()