What is the simplest way to create nested objects in Ebean? - java

I need two Ebean model classes called "States" and "Children". A "State" object can contain nested Child objects(List of children).
Here is the basic States class,
#Entity
public class States extends Model {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Constraints.Required(message = "stateName cannot be null")
#Column(nullable = false)
private String statename;
#Column(nullable = true)
private String url;
#Column(nullable = true)
private String parent;
private List<Children> childrenList;
}
Here is the basic Children class,
#Entity
public class Children extends Model {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false)
private String statename;
#Column
private String child;
}
What are the minimal modifications that should be done to these classes to create State objects using Ebean ORM? I went through the post,
Ebean Query by OneToMany Relationship
But there, a lot of changes have been suggested. I just want the minimal modifications.

All I had to do was, doing a small modification to the "States" class,
#Entity
public class States extends Model {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Constraints.Required(message = "stateName cannot be null")
#Column(nullable = false)
private String statename;
#Column(nullable = true)
private String url;
#Column(nullable = true)
private String parent;
#OneToMany(cascade = CascadeType.ALL)
private List<Children> childrenList;
}
Only change I have done here is,
#OneToMany(cascade = CascadeType.ALL)
I did not do any changes to the "Children" class. Before starting the play app I set
play.evolutions.enabled = true
in "application.conf" file. Then using the evolution SQL file that was created in "evolution.default" folder, I adjusted the schema of the database. After that "States" objects were created successfully with nested "Children" objects.

Related

Spring Boot, filtered OneToMany(mappedBy)

I have these two entities:
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "title")
private String title;
#OneToMany(mappedBy = "book")
private List<UserBook> userBookList;
}
And
public class UserBook {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "user_id")
private long userId;
#ManyToOne
#JoinColumn(name = "book_id", nullable = false)
private Book book;
#Column(name = "page_read")
private int pageRead;
}
What I would like to achieve is to retrieve the list of books from by postgre database and inject into userBookList (for every books) the object from the related UserBook entity given the current user that is sending the request. As of now, every time I get a list of books, userBookList would contain every item inside the UserBook table related to his specific book.
Is there a clean way to do it?
So what you have above is bi-directional mapping. If you take userBookList, it has multiple UserBooks referring to a book. If you just want a clean JSON output without circular referring, then add #JsonIgnore to the one you dont want to be repeated.

Relation between Entity and Object from service

I'm trying to make a relation between my Book entity and a list of languages that I retrieve through a service.
In my database, each book has a: ID, TITLE, CATEGORY_ID (FK), LANG_ID
Book.java:
#Entity
#Table(schema = Constants.SHEMA, name = "Book")
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private long id;
#Column(name = "TITLE")
private String title;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "CATEGORY_ID")
private Category category;
private Language language; // -> The Column associated in the database is Long LANG_ID
}
Category.java:
#Entity
#Table(schema = Constants.SHEMA, name = "Category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
}
Language.java:
public class Language implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
}
I understood the relation between Book & Category as both of them are tables in my database. However, Language is something that I get from a service and isn't persisted in my database.
The languages I get are just an ID and a Name for the language.
My question is: In order to link the language ID to my LANG_ID (the ID of the language in my Book table), what annotation (ManyToOne, Entity, ...) should I write for Language? Should I also put it in my persistence.xml ? I tried a couple but it seems like it's not working well.
Thank you very much
I don't think it is good practice to mix persisted data with non-persisted data as it can cause other unexpected problems. Anyway you can try something like this:
#Entity
#Table(schema = Constants.SHEMA, name = "Book")
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private long id;
#Column(name = "TITLE")
private String title;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "CATEGORY_ID")
private Category category;
#Column(name = "LANG_ID")
private Integer langId;
#Transient
private Language language;
#PostLoad
public void loadLanguage() {
// get the language data here
}
}
The language field has no database table, so you cannot use any mapping annotation. From the Java EE docs:
public #interface Transient
Specifies that the property or field is not persistent. It is used to annotate a property or field of an entity class, mapped superclass, or embeddable class.
Example:
#Entity
public class Employee {
#Id int id;
#Transient User currentUser;
...
}
The #PostLoad annotation declares a method to be called after the entity is loaded:
public #interface PostLoad
Specifies a callback method for the corresponding lifecycle event. This annotation may be applied to methods of an entity class, a mapped superclass, or a callback listener class.
First of all, did you consider to store language in your database? I mean language are mostly the same, doesn't change too often, you can also store in a properties file and read them at runtime to use them later.
Anyway, I think you should:
first get from external system languages
store in variable / in memory cache ( like a Map<Long,String> where you can store id and name )
read your data from database
for each row you do
read book language id, read the cache, get out data you need
If you can't change model, just use a dto with your entity and the language and you're fine

Hibernate not saving some properties

I'm working on a project using Spring Data JPA and Hibernate and one of my entities has a pretty weird behaviour: it's not saving some of the properties. The code below is my model class. It has some more properties and but all of them are String with the columnDefinition="TEXT". Debugging the object I'm sending to the repository all properties have values in it, get and set methods are working fine, etc.. but it only saves the title.
Now comes the weird part, if I do this Column(name = "isbn_v", columnDefinition="TEXT") it saves the data normally. The same for all columns that are not saving. I don't understand why. Model class:
#Entity
#Table(name = "notice")
public class Notice implements Serializable {
private static final long serialVersionUID = 4521230269805147556L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "seq_no", nullable = false, updatable = false)
private Integer id;
#Column(name = "title", columnDefinition="TEXT")
private String title;
#Column(name = "isbn", columnDefinition="TEXT")
private String isbn;
#Column(name = "issn", columnDefinition="TEXT")
private String issn;
#Column(name = "author", columnDefinition="TEXT")
private String author;
#Column(name = "publisher", columnDefinition="TEXT")
private String publisher;
// get / set
}
Repository class:
public interface NoticeRepository extends JpaRepository<Notice, Integer>, JpaSpecificationExecutor<Notice> {
#Override
#Modifying
#Transactional
Notice save(Notice notice);
}
Any clue of what I'm doing wrong?

How to add movie_id and user_id in "movie_added_by" table

I have already a user model.
Now I have created a movie model, my requirement is that whenever any existing user is going to add any movie, at that time user_id and movie_id will be store in the movie_added_by table.
Here user model needs to map one to many to movie_added_by and similarly, the movie will be mapped to movie_added_by.
For better understanding, you can refer to the DB diagram.
I really don't know how can I do by using hibernate annotation
The user model is like this:
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
}
The movie model is like this:
#Getter
#Setter
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
}
You probably want to create a #ManyToMany relationship between the entities. There are 2 ways of doing it (with intermediary table created explicitly or by Hibernate.
In simple approach your entities would look as following:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
#ManyToMany(cascade = CascadeType.Persist)
#JoinTable(name="user_movie",
joinColumns = {#JoinColumn(name="user_id")},
inverseJoinColumns = {#JoinColumn(name="movie_id)})
private Set<Movie> movies = new HashSet<>();
}
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
#ManyToMany(cascade = CascadeType.Persist, mappedBy = "movies" //field from the user class responsible for mapping)
private Set<User> users = new HashSet<>()
}
So basically here you tell Hibernate to create an intermediary table and keep there correlated id's of those 2 entities. Couple of other notes here:
a) you might want to change the id variable type from Integer to Long in case your entities grow;
b) If you have annotated a column with #Id, you don't have to use unique=true and nullable = false in the column annotation;
c) remember about implementing no-args constructor;
d) remember to exclude relationship fileds from the equals(), hashCode() and the toString() methods;
There is another way, where you explicitly create a model for the table keeping relationships. This might become handy, when it turns out that You need to keep more data in the 'relationship table'. In that case, Your entities would look as following:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
#OnetToMany(cascade = CascadeType.PERSIST, mappedBy = "user")
private Set<AddedMovie> addedMovies = new HashSet<>()
}
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "movie")
private Set<AddedMovie> moviesAddedByUser = new HashSet<>();
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Entity
public class AddedMovie{
#Id
#GeneratedValue
private Long id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "movie_id")
private Movie movie;
// sine this entity has now its own lifecycle, you can add more fields here
private Integer rating;
private LocalDateTime movieAddedOn;
}

spring data jpa find all by example nested collection property

I have two objects. The company that can have multiple nested addresses.
#Entity
#Data
#Table(name = "company")
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "phone")
private String phone;
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Address> addresses;
}
Address class looks like this:
#Data
#Entity
#Table(name = "address")
#ToString(exclude = "company")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "postal_code")
private String postalCode;
#Column(name = "city")
private String city;
#Column(name = "street")
private String street;
#JsonIgnore
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "company_id")
private Company company;
}
I want somehow if it's possible, make a dynamic query that searches through the nested collection property. I made a search method which uses example matcher but the result is wrong. Every time I got everything from DB, not only company with address postal code that I'm looking for.
My search method looks like this:
#PostMapping("/search")
public List<Company> search(#RequestBody final Company company){
return companyRepository.findAll(Example.of(company,
ExampleMatcher.matchingAny()
.withIgnoreNullValues()
.withIgnorePaths("id")
.withStringMatcher(ExampleMatcher.StringMatcher.STARTING)));
}
In my database, I have two objects and this is the result of the search:
As you can see I received everything from DB instead of the only first company which address postal code starts with 1.
Hi you can use Specification<T>
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
For this you need to extend from interface JpaSpecificationExecutor:
public interface UserRepository extends JpaRepository<User> ,JpaSpecificationExecutor<User>{
}
And you also need to implement your custom Specification<T>
And then you can use repository.findAll(your impleneted Specification);
Spring docs :
https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaSpecificationExecutor.html
I think this is helpful.

Categories

Resources