persisting a list of objects in play framework - java

I have a class called Device.class and i want to have a field that is a list of CommentObj.class . the problem is that this field is not persisted in database (I'm using Ebean). how is it possible to do so?
#Entity
public class Device extends Model {
#Id
public Long id;
#Constraints.MaxLength(50)
#Constraints.Required
public String serialNo;
...
public List<CommentObj> comments = new ArrayList<CommentObj>();
and
public class CommentObj extends Model {
#Id
public Long id;
#Constraints.MaxLength(50)
public String author;
#Constraints.MaxLength(500)
#Constraints.Required
public String content;
#Constraints.Required
public Date date;
public static Finder<Long, CommentObj> find = new Finder<>(Long.class, CommentObj.class);
public CommentObj(String author, String content){
this.author = author;
this.content = content;
date = new Date();
}
}
here is the structure of Device in database (according to 1.sql). there is no sign of comments field
create table device (
id bigint not null,
serial_no varchar(255),
invent_no varchar(255),
name varchar(255),
device_type_id bigint,
description varchar(2000),
cal_date timestamp,
cal_duration_in_months integer,
setup_id bigint,
manufacturer_id bigint,
constraint pk_device primary key (id))
;

Assuming you do have a device_id in your comment_obj table, you can have a unidirectional OneToMany mapping by adding the following annotations to your Device.class' comments field:
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(referencedColumnName = "id", name = "device_id")
public List<CommentObj> comments = new ArrayList<CommentObj>();

Related

Hibernate trying to alter table before creating it

Simple issue here: I'm running Spring Boot 2.2.5 on a mySQL database with MySQL5Dialect. Everything was peachy until I've added #ManyToOne annotation to Slide entity referencing the User entity - now Hibernate can't create tables because it creates the users table and then tries to alter slides table which it hasn't created yet. What did I do wrong?
User:
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private boolean enabled;
private String token;
private String username;
private String password;
private String role;
private String name;
private String surname;
private String email;
private boolean emailVisible;
private String phone;
private boolean phoneVisible;
private int cohortNumber;
private String company;
private String position;
private String linkedIn;
private String function;
private String bio;
private String membership;
private Date membershipSince;
private Date membershipUntil;
}
Slide:
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString(exclude = "editor")
#Table(name = "slides")
public class Slide {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private boolean visible;
private int order;
private Date edited;
#ManyToOne
#JoinColumn(name = "editor_id")
private User editor;
private String title;
private String text;
private String picture;
}
Hibernate config specifics:
spring.jpa.hibernate.ddl-auto = update
spring.datasource.initialization-mode=always
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
And the error:
Hibernate: create table users (id integer not null auto_increment, bio varchar(255), cohort_number integer not null, company varchar(255), email varchar(255), email_visible bit not null, enabled bit not null, function varchar(255), linked_in varchar(255), membership varchar(255), membership_since datetime, membership_until datetime, name varchar(255), password varchar(255), phone varchar(255), phone_visible bit not null, position varchar(255), role varchar(255), surname varchar(255), token varchar(255), username varchar(255), primary key (id)) engine=MyISAM
Hibernate: alter table slides add constraint FKobqxptfxg36ls278o63ouq369 foreign key (editor_id) references users (id)
2020-08-11 14:27:48.201 WARN 8224 --- [ restartedMain] o.h.t.s.i.ExceptionHandlerLoggedImpl : GenerationTarget encountered exception accepting command : Error executing DDL "alter table slides add constraint FKobqxptfxg36ls278o63ouq369 foreign key (editor_id) references users (id)" via JDBC Statement
...
Caused by: java.sql.SQLSyntaxErrorException: Table '32293814_alumnicemba.slides' doesn't exist
Found the problem:
private int order;
"order" is not allowed as a field name here and Hibernate was encountering an error when trying to create the slides table but not logging that error. I've renamed the field to "sorting" and it works now.

Hibernate Save only Child table entry with parent foreign Key in Composite primary key

I am starter in Hibernate & Spring MVC and struggling to find correct solution to my problem.
I have Parent Table (Events)and Child table (Votes). I want to save only child table entry whenever data is received from URL. I am having trouble in mapping relations and putting composite key to work
Following is my structure:
create table Events(
Event_ID int(11) NOT NULL AUTO_INCREMENT,
Event_Name varchar(200) NOT NULL,
Event_options int(1) NOT NULL,
Start_TIME timestamp,
End_time timestamp,
Active_Status int(1),
PRIMARY KEY(Event_ID)
)ENGINE=InnoDB AUTO_INCREMENT=16
;
create table Votes(
Event_ID int(11) NOT NULL,
Voter_MSISDN int(13) NOT NULL,
Vote_Option int(1),
PRIMARY KEY(Event_ID,Voter_MSISDN),
FOREIGN KEY (Event_ID) REFERENCES Events(Event_ID)
)ENGINE=InnoDB AUTO_INCREMENT=16
;
Events.java
#Entity
#Table(name="Events")
public class Events {
#Id
#GeneratedValue
#Column(name = "Event_ID")
private Integer eventId;
#Column(name="Event_Name")
private String eventName;
#Column(name="Event_options")
private Integer eventOptions;
#Column(name="Start_TIME")
private String startTime;
#Column(name = "End_time")
private String End_time;
#Column(name="Active")
private Integer status;
#OneToMany(mappedBy = "Events")
private Set<Votes> votes;
//Setter Getters
}
Votes.java
#Entity
#Table(name="Votes")
public class Votes {
public Votes(){}
#EmbeddedId
private Vote vote;
#Column(name = "Vote_Option")
private int Vote_Option;
#ManyToOne
#JoinColumn(name = "Event_ID")
private Events events;
//setters getters
}
Vote.java for Composite primary key setup through #Embeddable
#Embeddable
public class Vote implements Serializable{
public Vote(){}
#Column(name="Event_ID")
private int Event_ID;
#Column(name="Voter_MSISDN")
private long Voter_MSISDN;
//setter getters
}
controller snippet for Adding Event and adding Vote
#RequestMapping(value="/AddEvent")
#ResponseStatus(value = HttpStatus.OK)
public void AddEvent(#RequestParam(value = "ename", required = true) String ename,
#RequestParam (value = "opt") String opt,
#RequestParam (value = "stime") String start,
#RequestParam (value = "etime") String end,
#RequestParam (value = "status") String active){
Events event = new Events();
event.setEventName(ename);
event.setEventOptions(Integer.parseInt(opt));
event.setStartTime(start);
event.setEnd_time(end);
event.setStatus(Integer.parseInt(active));
userDao.saveEvent(event);
}
#Autowired
private Vote vote;
#Autowired
private Votes votes;
#RequestMapping(value="/AddVote")
#ResponseStatus(value = HttpStatus.OK)
public void AddVote(#RequestParam(value = "eventid",required = true) String eventid,
#RequestParam(value="msisdn") String msisdn,
#RequestParam(value = "opt")String opt){
logger.info("Received parameters from URL "+eventid+" "+msisdn+" "+opt);
vote.setEvent_ID(Integer.parseInt(eventid));
vote.setVoter_MSISDN(Long.parseLong(msisdn));
votes.setVote(vote);
votes.setVote_Option(Integer.parseInt(opt));
userDao.saveVotes(votes);
}
}
DAOImplementation:
#Transactional
public void saveEvent(Events event) {
// TODO Auto-generated method stub
Session session = sessionFactory.getCurrentSession();
session.save(event);
}
#Transactional
public void saveVotes(Votes votes){
Session session = sessionFactory.getCurrentSession();
session.save(votes);
}
The code is working fine whenever Event data is received and Event entry is added.
Not able to code correctly for Vote data.
I want to insert just vote data whenever it is received from url. I have added Composite primary key to make sure unique entry for each event from one user(MSISDN).
Please suggest correct mapping for this model.
Any improvement suggestions are also welcome.
First of all you don't need a composite key for this simple solution.
You have a class:
#Entity
#Table(name="Events")
public class Events {
//....................
#OneToMany(cascade = CascadeType.ALL, mappedBy = "Events")
private Set<Votes> votes = new HashSet<>();
}
Simple just create a method in it which would create a new vote for that event. For e.g.:
public Vote createVote() {
Vote vote = new Vote();
vote.setEvent(this);
votes.add(vote);
return vote;
}
Then in your addVote controller method:
#RequestMapping(value="/AddVote")
#ResponseStatus(value = HttpStatus.OK)
public void AddVote(#RequestParam(value = "eventid",required = true) String eventid,
#RequestParam(value="msisdn") String msisdn,
#RequestParam(value = "opt")String opt){
Event event = userDao.findEvent(eventid);
Vote vote = event.createVote(); //This will create a vote for an event.
vote.set.... //set your stuff.
//It will cascade your vote to an event if you have a cascade sorted correctly as in the example above: cascade = CascadeType.ALL
}
Just make sure you got your transactions right. That's just an idea how it should be done.

What Hibernate annotations am I missing?

I'm brand new to Hibernate, and I'm trying to get a fairly simple Hibernate code snippet to work. After reading the tutorials, I'm totally choking on the full implementation.
For one, when it comes to the hbm2ddl.auto property, I'm setting it to validate because I just don't like the idea of Hibernate creating my table structure (I'm old fashioned; perhaps that will change as I become more comfortable with Hibernate though). In any event, here's the table I just created on a MySQL server:
CREATE TABLE users (
id INT NOT NULL AUTO INCREMENT,
email VARCHAR(200) NOT NULL,
title VARCHAR(25),
first_name VARCHAR(100),
middle_name VARCHAR(100),
last_name VARCHAR(100),
suffix VARCHAR(100),
PRIMARY KEY (id)
);
Thise corresponds to the following POJOs/entities in my app's code:
#Entity
#Table(schema="my_db", name="users")
public class User {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Integer id;
#Column(name="email")
private String email;
// ???
private PersonName personName;
public User(final String email, final PersonName personName) {
super();
setEmail(email);
setPersonName(personName);
}
// Getters and setters omitted for brevity...
}
public abstract class BaseName {
public abstract String toName();
#Override
public String toString() {
return toName();
}
}
public class PersonName extends BaseName {
private String title;
private String firstName;
private String middleName;
private String lastName;
private String suffix;
public PersonName(final String title, final String firstName, final String middleName, final String lastName, final String suffix) {
super();
setTitle(title);
setFirstName(firstName);
setMiddleName(middleName);
setLastName(lastName);
setSuffix(suffix);
}
// Getters and setters omitted for brevity...
}
What annotations/config do I need to add so that User#personName gets persisted as an embedded PersonName object inside the users table? In other words, User is an entity and contains a PersonName as an embedded objects (non-entity).
Also, any other obvious annotations I'm missing? Thanks in advance!
As suggested by user #Snow Blind, Embedded objects are what you want.
Let's start with class PersonName:
#Embeddable
public class PersonName extends BaseName {
#Column(name = "title")
private String title;
#Column(name = "first_name")
private String firstName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "last_name")
private String lastName;
#Column(name = "suffix")
private String suffix;
// ...
}
Now add the #Embedded tag here:
// ...
#Embedded
private PersonName personName;
// ...
Hope this helps!
You can check the title 2.2.2.4. Embedded objects (aka components) in docs:
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping-property

Cascade insert in hibernate and unique constraint on cascading table

I have a following SQL schema layout:
-- postgresql82+ syntax
create table AudioTracks (
id serial primary key
, name text
, size integer
, filePath text
, additionDate timestamp default now()
);
create table Genres (
id serial primary key
, name text unique -- here is the unique constraint which troubles me
, description text
, additionDate timestamp default now()
);
create table AudioTrackGenre (
genreId integer references Genres (id) unique
, audioTrackId integer references AudioTracks (id) unique
, additionDate timestamp default now()
);
And two corresponding mappings to tables:
#Entity(name = "AudioTracks")
public class AudioTrack implements Serializable {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column
private String name;
#Column
private Integer size;
#Column
private String filePath;
#Column
#Temporal(TemporalType.TIMESTAMP)
private Date additionDate;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
#JoinTable(name = "AudioTrackGenre",
joinColumns = { #JoinColumn(name = "audioTrackId") },
inverseJoinColumns = { #JoinColumn(name = "genreId") }
)
#OrderBy("name")
private Set<Genre> genres = new HashSet<Genre>();
// setter/getter methods //
....
}
and
#Entity(name = "Genres")
public class Genre implements Serializable {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column
private String name;
#Column
private String description;
#Column
#Temporal(TemporalType.TIMESTAMP)
private Date additionDate;
#ManyToMany(mappedBy = "genres")
private Set<AudioTrack> audioTracks = new HashSet<AudioTrack>();
public Genre() { }
public Genre(String name) { this.name = name; }
// setter/getter methods //
....
}
But whenever i am trying to save AudioTrack, populated with Genres which are already exists in Genres table, like here:
Set<Genre> genres = new HashSet<Genre>();
genres.add(new Genre("ambient"));
AudioTrack track = new AudioTrack();
track.setGenres(genres);
audioTrackService.addAudioTrack(track);
(the audioTrackService.addAudioTrack(track) thing does sessionFactory.getCurrentSession().save(track) at lowest DAO level)
i am getting:
ERROR: ERROR: duplicate key value violates unique constraint "genres_name_key"
Detail: Key (name)=(ambient) already exists.
How do i tell Hibernate not to try to re-insert already existing genres to Genres table on cascade inserts?
If the genre already exists, you must provide its id.
Look at this line: new Genre("ambient").
How could hibernate possibly guess what is the existing id of the Genre ambient?
Hibernate tries to insert the corresponding genre, because you haven't provided its id.
When you insert the audio track, hibernate must inserts records in the AudioTrackGenre table. Hibernate must know the ids of the genres. Otherwise hibernate assumes they are new genres.
Edit:
It seems you are adding genres on demand(like StackOverflow tags).
You can do the following in your code:
for (String genreName : submittedTextGenres) {
Genre genre = genreDAO.findByName(genre);
if (genre == null) { //a new genre
genre = new Genre(genreName);
}
audioTrack.addGenre(genre);
}
If you are afraid of a concurrent user adding the same genres: (suggestion by JB Nizet)
for (String genreName : submittedTextGenres) {
Genre genre = genreDAO.findByName(genre);
if (genre == null) { //a new genre
try {
genre = genreDAO.insertGenre(genre); //a transaction
} catch (GenreExistsException) {
genre = genreDAO.findByName(genre); //a separate transaction
}
}
audioTrack.addGenre(genre);
}

JPA Hibernate - Mapping MySQL Composite keys to JPA (Hibernate) entities

I am trying to map the following table
CREATE TABLE Person (
p_id varchar(255) not null,
p_name varchar(255 not null,
p_post_code varchar(12) not null,
primary key (p_id, p_name),
);
Usually when i map an Entity to the above table i would do something like this (for single column primary keys):
private int p_id;
private String p_name;
private String p_post_code;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="p_id")
public Long getPId() {
return p_id;
}
public void setPId(int p_id) {
this.p_id = p_id;
}
#Column(name="p_name")
public String getPname() {
return p_name;
}
public void setPname(String string) {
this.p_name = string;
}
#Column(name="p_post_code")
public String getPostCode() {
return p_post_code;
}
public void setPostCode(String string) {
this.p_post_code = string;
}
The above works if the primary key is a single column (i.e. p_id) and the value for this column is generated in the database.
How would i modify the above to map it so that both p_id and p_name are the primary key.
Also, how would this work, if the composite key is a foreign key in another table.
I am trying to google for some examples but i cant find a simple example and most seem to be using the XML based configuration.
When using composite keys with JPA you need to use an embedded class as an id.
In your case you would have a person class and a primary key class for person:
#entity
public class Person
{
#EmbeddedId
private PersonPK key;
#Column(name="p_post_code", nullable = false)
private String p_post_code;
//....
}
#Embeddable
public class PersonPK
{
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="p_id");
private Long p_id;
#Column(name="p_name")
private String p_name;
public PersonPK(String name)
{
p_name = name;
}
//....
}
Using a class for the person's name (so the name is also a foreign key):
#entity
public class Person
{
#EmbeddedId
private PersonPK key;
#MapsId(value="p_name_id")
#ManyToOne
#JoinColumn(name = "p_name_id", referencedColumnName = "id")
private Name p_name;
#Column(name="p_post_code", nullable = false)
private String p_post_code;
//....
}
#Embeddable
public class PersonPK
{
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="p_id");
private Long p_id;
#Column(name="p_name_id")
private Long p_name_id;
public PersonPK(Name name)
{
p_name_id = name.getId();
}
//....
}
#Entity
public class Name
{
#Id
#GeneratedValue(some generation strategy here)
#Column(name="id")
private Long id;
#Column(name="name")
private String name;
//....
}

Categories

Resources