Based on my research we have two way for getting related data from two or more tables.
For example if we have 2 tables like below:
#Entity
public class User {
#PrimaryKey public long userId;
public String name;
public int age;
}
#Entity
public class Library {
#PrimaryKey public long libraryId;
public long userOwnerId;
}
If we want to load all data we have two options:
1. #Embedded and #Relation
By adding this class:
public class UserAndLibrary {
#Embedded public User user;
#Relation(
parentColumn = "userId",
entityColumn = "userOwnerId"
)
public Library library;
}
And add DAO method:
#Transaction
#Query("SELECT * FROM User")
public List<UserAndLibrary> getUsersAndLibraries();
More information in Android Documentation
2. #DatabaseView
#DatabaseView("SELECT user.id, user.name, user.age, " +
"library.libraryId FROM user " +
"INNER JOIN library ON user.userId = library.libraryId)
public class UserAndLibrary {
public long userId;
public String name;
public int age;
public long libraryId;
}
and a associating
#Database(entities = {User.class, Library.class},
views = {UserAndLibrary.class},
version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
What is the difference between two options?
Related
Gotta question regarding mapStruct. I have case where I extend class from base entity and not sure how to map it. Here is my case.
BaseEntity:
public class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id")
private Long id;
}
BaseDto:
public class BaseDto {
private Long id;
}
UserEntity:
public class User extends BaseEntity {
private String name;
private String lastName;
private String username;
private String password;
private String profilePicturePath;
}
UserDto:
public class UserDto extends BaseDto {
private String name;
private String lastName;
private String username;
private String password;
private String profilePicturePath;
}
And mapper is like this:
#Mapper(uses = {BaseMapper.class})
public interface UserMapper {
User userDtoToUser(UserDto userDto);
UserDto userToUserDto(User user);
}
BaseMapper:
#Mapper
public interface BaseMapper {
BaseEntity dtoToEntity(BaseDto baseDto);
BaseDto entityToDto(BaseEntity baseEntity);
}
Problem is that I don't get ID property mapped.
Thank you for your time.
EDIT:
There is no error shown, in mapper implementation (generated code) there is no mapping for that ID:
#Override
public User userDtoToUser(UserDto userDto) {
if ( userDto == null ) {
return null;
}
UserBuilder user = User.builder();
user.name( userDto.getName() );
user.lastName( userDto.getLastName() );
user.username( userDto.getUsername() );
user.password( userDto.getPassword() );
user.profilePicturePath( userDto.getProfilePicturePath() );
return user.build();
}
I'm guessing (as you have not put buider code) the problem is that your builder class does not include parent class field. MapStruct makes some assumption while generating code for mapper. From documentation -
The default implementation of the BuilderProvider assumes the
following:
The type has a parameterless public static builder creation method
that returns a builder. So for example Person has a public static
method that returns PersonBuilder.
The builder type has a parameterless public method (build method)
that returns the type being build In our example PersonBuilder has a
method returning Person.
In case there are multiple build methods, MapStruct will look for a
method called build, if such method exists then this would be used,
otherwise a compilation error would be created.
If you are using Lombok, you can solve this by using #SuperBuilder as -
#SuperBuilder
#Getter
#ToString
public class UserDto extends BaseDto {
private String name;
private String lastName;
private String username;
private String password;
private String profilePicturePath;
}
#Getter
#SuperBuilder
class BaseDto {
private Long id;
}
#SuperBuilder
#Getter
#ToString
public class User extends BaseEntity {
private String name;
private String lastName;
private String username;
private String password;
private String profilePicturePath;
}
#Setter
#Getter
#SuperBuilder
class BaseEntity {
private Long id;
}
And generated could looks like -
#Override
public User userDtoToUser(UserDto userDto) {
if ( userDto == null ) {
return null;
}
UserBuilder<?, ?> user = User.builder();
user.id( userDto.getId() );
user.name( userDto.getName() );
user.lastName( userDto.getLastName() );
user.username( userDto.getUsername() );
user.password( userDto.getPassword() );
user.profilePicturePath( userDto.getProfilePicturePath() );
return user.build();
}
JAVA SPRING :I am exploring JPA and am not sure of optimized way to design db and using save() of repository to save entity data right away into DB. Specifically, I have a basic class viz. Movie -
package com.kurshit.moviesmgr.vo;
import java.util.List;
public class Movie {
long movieId;
String title;
String yearOfRelease;
List<String> genere;
public Movie(long movieId, String title, String yearOfRelease, List<String> genere) {
super();
this.movieId = movieId;
this.title = title;
this.yearOfRelease = yearOfRelease;
this.genere = genere;
}
public Movie() {
// TODO Auto-generated constructor stub
}
public long getMovieId() {
return movieId;
}
public void setMovieId(long movieId) {
this.movieId = movieId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getYearOfRelease() {
return yearOfRelease;
}
public void setYearOfRelease(String yearOfRelease) {
this.yearOfRelease = yearOfRelease;
}
public List<String> getGenere() {
return genere;
}
public void setGenere(List<String> genere) {
this.genere = genere;
}
}
Each movie has list of Genere - List- it falls under, like - Action, Comedy, etc.
I am trying to create an interface that extends JpaRepository and use the inbuilt save method to save the Movie Data into DB.
I am not sure about how I should design my DB - As in, Questions like -
1. Shall I create two different tables for Movie and Genere wherein Movie table references to Genere ?
2. Shall I create just onw table and store all Genere's list as a single CSV in one column ?
3. Can I use repository's save() right away to save and map this data into respective tables.
Would really appreciate if someone can share any sources or sample code to refer or can offer any help.
Thanks much!
First of all, you should search look up #Entity annotation so that you can tell your ORM to create the necesary table for that entity.
Secondly, you need to ask yourself, how this application will work. It would be best in my opinion to create a genre entity as well, linked to Movie through a #ManyToMany relationship.
Try looking over the simple entity example here
https://spring.io/guides/gs/accessing-data-jpa/
First variant - with 'genre' as enum (if your genre is a fixed list):
#Data // it's Lombok annotation: https://projectlombok.org/features/Data
#NoArgsConstructor
#Entity
public class Movie implements Serializable {
#Id #GeneratedValue
private Integer id;
private String title;
private Integer yearOfRelease;
#ElementCollection
#Enumerated(EnumType.STRING)
#Column(name = "genre")
private List<Genre> genres;
public Movie(String title, Integer yearOfRelease, List<Genre> genres) {
this.title = title;
this.yearOfRelease = yearOfRelease;
this.genres = genres;
}
}
public enum Genre {
ACTION, COMEDY, ...;
}
public interface MovieRepo extends JpaRepository<Movie, Integer> {
}
In this case you create your movie like this:
Movie movie = new Movie("Title", 2000, Arrays.asList(ACTION, COMEDY));
movieRepo.save(movie);
Second variant - 'genre' as independent entity:
#Data
#NoArgsConstructor
#Entity
public class Movie implements Serializable {
// the same as previous one...
#ManyToMany
private List<Genre> genres;
}
#Data
#NoArgsConstructor
#Entity
public class Genre implements Serializable {
#Id private String name;
public Genre(String name) {
this.name = name
}
}
public interface MovieRepo extends JpaRepository<Movie, Integer> {
}
public interface GenreRepo extends JpaRepository<Genre, String> {
}
In this case you first create genres:
List<Genre> genres = genreRepo.saveAll(Arrays.asList(
new Genre("Action"),
new Genre("Comedy"),
));
Then create movie:
Movie movie = new Movie("Title", 2000, genres);
movieRepo.save(movie);
More info to read: Hibernate ORM User Guide - Collections
I'm trying to use the new Architecture components, but when I try to run, I get :
"Error:(375, 24) error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such table: posts)"
The following are my classes.
**ENTITY : **
#Entity
public static class Post {
#PrimaryKey
private String id;
#ColumnInfo(name = "data")
private String data;
public String getId() {
return id;
}
public void setData(String data) {
this.data = data;
}
public String getData() {
return data;
}
public void setId(String id) {
this.id = id;
}
}
DAO :
#Dao
public interface PostDao {
#Query("SELECT * FROM posts")
LiveData<List<Post>> getAll();
#Insert
void insertAll(Post... posts);
#Insert
void insert(Post post);
#Delete
void delete(Post post);
}
The database :
#Database(entities = {Post.class}, version = 1)
public static abstract class AppDatabase extends RoomDatabase {
public abstract PostDao postDao();
}
By default, Room uses the class name as the database table name. If you want the table to have a different name, set the tableName property of the #Entity annotation, as shown in the following code snippet:
https://developer.android.com/topic/libraries/architecture/room.html
It seems you assumed it would pluralize the class on its own.
So, either use SELECT * FROM Post
or do
#Entity(tableName = "posts")
class Post {
...
}
I am creating an application that uses a many to many relationship between employees and shifts. However, I am having difficulties in understanding how I can assign/connect an employee to a shift.
#Data
#Entity
public class Employee {
private #Id #GeneratedValue long employeeID;
private String Name;
#OneToMany(cascade = CascadeType.ALL)
private Set<Shift> shifts;
private Employee() {
}
public Employee(long employeeID, String Name) {
this.employeeID = employeeID;
this.Name = Name;
}
public Employee(long employeeID, String Name, Set<Shift> shifts) {
this.employeeID = employeeID;
this.Name = Name;
this.shifts = shifts;
}
public void setShift(Set<Shift> shifts) {
this.shifts = (Set<Shift>) shifts;
}
}
#Data
#Entity
public class Shift {
private #Id #GeneratedValue long Id;
private String shifts;
private Set<Employee> employee;
private Shift() {
}
public Shift(String shifts) {
this.shifts = shifts;
}
public Shift(String shiftPeriod,Set<Employee> employee ) {
this.shifts = shifts;
this.employee=employee;
}
public void setEmployee(Set<Employee> employee) {
this.employee = employee;
}
}
#Component
public class DatabaseLoader implements CommandLineRunner {
private final EmployeeRepository repository;
#Autowired
public DatabaseLoader(EmployeeRepository repository) {
this.repository = repository;
}
#Override
public void run(String... strings) throws Exception {
Shift shift = new Shift("Friday Morning");
Employee employee = new Employee(0001, "Adam Smith");
employee.setShift(shift);
this.repository.save(employee);
}
}
public interface ShiftRepository extends CrudRepository<Shift, Long>
public interface EmployeeRepository extends CrudRepository<Employee, Long>
Entities added into employees and shifts are saved but is there a way I can assign a shift to an employee in the DatabaseLoader class, as I've been stuck on finding a solution to this.
I know that I haven't included a method which attempts to connect employee and shifts but I don't how to approach this problem.
Thanks in advance
**EDIT: The new problem I have now is that I get the following message when trying to deploy in spring:
Unable to build Hibernate SessionFactory: Could not determine type for: java.util.Set, at table: shift, for columns: [org.hibernate.mapping.Column(employee)]
In my opinion:
A shift is an independent entity. It does not depend on any employees.
So Shift must not have a reference to Employee.
On the other hand an employee depend on several shifts, so Employee must have unidirectional #OneToMany association with Shift.
#OneToMany
private List<Shift> shifts;
And don't use cascading here because Employee and Shift are independent entities.
UPDATE
To "add a shift to an employee" we can use for example an Employee setter:
#Entity
public class Employee {
//...
private String name;
//...
#OneToMany
private List<Shift> shifts;
//...
public Employee(String name) {
this.name = name;
}
//...
public setShifts(Shift... shifts) {
this.shifts = Arrays.asList(shifts);
}
//...
}
then
Shift monday = shiftRepository.save(new Shift("Monday"));
Shift friday = shiftRepository.save(new Shift("Friday"));
Employee adamSmith = new Employee("Adam Smith");
adamSmith.setShifts(monday, friday);
employeeRepository.save(adamSmith);
It seems that you are using Hibernate under the hood, then all you have to do is to set the objects you want to save properly, like this:
#Component
public class DatabaseLoader implements CommandLineRunner {
private final EmployeeRepository repository;
private final ShiftRepository shiftRepository;
#Autowired
public DatabaseLoader(EmployeeRepository repository, ShiftRepository shiftRepository) {
this.repository = repository;
this.shiftRepository=shiftRepository;
}
#Override
public void run(String... strings) throws Exception {
Shift shift = new Shift("Friday Morning");
Employee employee = new Employee(0001, "Adam Smith");
employee.setShift(shift)
this.repository.save(employee);
}
The only difference is that I attached between the two objects and only then I try to save them. It's worth to mention that it's important to use Cascade.All or Cascade.persiste in order that hibernate will do the inserts on both entities.
Basic Hibernate question.
I have a class called Song and a class called Artwork, both exist independently. Then an instance of Song can contain multiple Artworks and when they do there are attribute particular to that relationship so I have created another class called CoverArt that links between the two. I'm using annotations for the hibernate stuff and having problems.
If I annotate all three classes as #Entity when I build the database I get the error >'org.hibernate.MappingException: Could not determine type for: Artwork, at table: CoverArt, for columns: [org.hibernate.mapping.Column(artwork)]'
If I change CoverArt to #Embeddable, as it only exists in the context of a Song I get the error
'org.hibernate.annotations.common.AssertionFailure: Declaring class is not found in the inheritance state hierarchy: com.jthink.songlayer.CoverArt'
I can't work out what these messages are saying, what I have wrong. Here is the relevant code from the three classes
Song:
#Entity
public class Song
{
#Id
#GeneratedValue
private Integer recNo;
#ElementCollection(fetch=FetchType.EAGER)
#IndexColumn(name = "POSITION")
private List<CoverArt> coverArt;
.....
CoverArt:
#Embeddable
public class CoverArt
{
private String imageType;
private String description;
private Artwork artwork;
#Id
#GeneratedValue
private Integer id;
public CoverArt()
{
}
public String getImageType()
{
return imageType;
}
public void setImageType(String imageType)
{
this.imageType = imageType;
}
public String getDescription()
{
return description;
}
public void setDescription(String description)
{
this.description = description;
}
public Artwork getArtwork()
{
return artwork;
}
public void setArtwork(Artwork artwork)
{
this.artwork = artwork;
}
}
Artwork:
#Entity
public class Artwork
{
public Artwork()
{
}
public Artwork(byte[] imageData)
{
this.imageData=imageData;
}
#Id
#GeneratedValue
private Integer id;
#Lob
private byte[] imageData;
private String mimeType;
private int width;
private int height;
public byte[] getImageData()
{
return imageData;
}
public void setImageData(byte[] imageData)
{
this.imageData = imageData;
}
public String getMimeType()
{
return mimeType;
}
public void setMimeType(String mimeType)
{
this.mimeType = mimeType;
}
public int getWidth()
{
return width;
}
public void setWidth(int width)
{
this.width = width;
}
public int getHeight()
{
return height;
}
public void setHeight(int height)
{
this.height = height;
}
}
The CoverArt class should be an entity.
The Song has a list of CoverArt instances, you should thus have
#OneToMany
#JoinColumn(...)
private List<CoverArt> coverArts; // note the final s, since it's plural
Each CoverArt links to an Artwork, so you should also have an association. It's not clear if it's a ManyToOne or a OneToOne, tough. I'll suppose it's a OneToOne:
#OneToOne
#JoinColumn(...)
private Artwork artwork;
It's pretty simple. Each time an entity has a reference to another entity, or a collection of another entity instances, you have an assosiation. And an association can be a OneToMany, OneToOne, ManyToOne or ManyToMany. You have to tell Hibernate which one it is. If you don't tell it, it assumes it's a simple Column which is wrong.
First of all You should tell us how do You want all that to look like in database.
I assume You want something like that:
table 'songs'
table 'artworks'
table 'cover_arts' with fkeys: song_id and artwork_id
So a Song "has many" CoverArts and each CoverArt "has one" Artwork.
If this is correct, then:
Annotate CoverArt with #Entity instead of #Embeddable
inside CoverArt class annotate field 'artwork' with #ManyToOne
replace #ElementCollection on field 'coverArt' inside Song class with #OneToMany. It would be nice to rename field 'coverArt' to 'coverArts' as it is a collection, not a single instance.