Entity mapping to DTO - spring-boot

I want to map query result to DTO of the following JPQL:
#Repository
public interface FooRepository extends JpaRepository<Foo, Id> {
#Query("select f.game, sum(f.timeSpent) as duration from foo f group by f.game order by duration desc")
List<Foo> findMostPlayable();
}
As a result, I receive list of objects which consist of GameCatalog object and Long number:
0 = {Object[2]#10670}
0 = {GameCatalog#10675}
1 = {Long#10676} 8968
Foo.class looks like:
#Entity
#Getter
#Setter
public class Foo{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "game_catalog_id", nullable = false)
private GameCatalog game;
private Long timeSpent;
}
I plan to use MapStruct to map model with DTO but I cannot do that since 'findMostPlayable' returns result in such a way above.
How I can implement mapping here?
And should I use JPA JPQL way or hibernate features like projections and so on?

To get result of query with aggregate function,you can create your own class instead of using entity and use that.
Ex.
public class MyFoo {
private String game;
private int duration;
public String getGame() {
return game;
}
public void setGame(String game) {
this.game = game;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
}
#Repository
public interface FooRepository extends JpaRepository<Foo, Id> {
#Query("select new MyFoo(f.game as game, sum(f.timeSpent) as duration ) from foo f group by f.game order by duration desc")
List<MyFoo> findMostPlayable();
}

Related

How Can I mapping DTOs using mapstruct?

I am tring to mapping entity datas to DTOs using mapstruct.
And with these sources, I could map id,title datas.
But the problem is.... I can not map userName using these sources.
How can I resolve this problem??
#Entity // DB와의 연결을 위하여
#Data // getter setter
public class Board {
#Id // id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min=2, max=30)
private String title;
#Length(min=20)
#Lob
#Column(columnDefinition="TEXT", nullable = false)
private String content;
#ManyToOne
#JoinColumn(name="userId", referencedColumnName = "id")
private User user;
}
#Builder
#AllArgsConstructor
#Data
public class BoardListDto {
private Long id;
private String title;
private String userName;
}
#Mapper(componentModel = "spring")
public interface BoardListMapper extends EntityMapper<BoardListDto, Board> {
#Override
#Mapping(target = "userName", source = "user.name.value")
List<BoardListDto> toDtos(List<Board> board);
}
public interface EntityMapper <D, E> {
E toEntity(D dto);
D toDto(E entity);
// Entity업데이트 시 null이 아닌 값만 업데이트 하도록 함.
#BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void updateFromDto(D dto, #MappingTarget E entity);
List<D> toDtos(List<E> entity);
}
no need to implement toDtos method for this. This code should be enough and Mapstruct will handle the rest alone.
#Mapper(componentModel = "spring")
public interface BoardListMapper extends EntityMapper<BoardListDto, Board> {
#Override
#Mapping(target = "userName", source = "user.name")
BoardListDto toDto(Board board);
}

Get records for last 3 days via Spring JPA Repository

I have an entity which contains field date.
#Entity
#Table(name="messages", schema = "users")
...
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "author")
private String author;
#Column(name = "tags")
private String tags;
#Column(name = "message_date")
private LocalDate date;
#Override
public String toString() {
...
}
}
#Repository
public interface MessageRepository extends JpaRepository<Message, Long> {
Message findByMessageId(Long id);
}
I'm using Spring Data JPA with repository. I want to get all messages from database for last 3 days (field date). How can I do it with Spring JPA?
#Query(...?)
List<Message> findBy...?
I suggest to split the logic from the actual queries. A service could handle all the intermediate things, e.g.:
#Service
public class MessageService {
private final MessageRepository repository;
#Autowired
public MessageService(MessageRepository repository) {
this.repository = repository;
}
List<Message> getLastThreeDays() {
// subtract 3 days from today
LocalDate threeDaysAgoDate = LocalDate.now().minusDays(3);
return this.repository.findAllWithDateAfter(threeDaysAgoDate);
}
}
and your repository stays nice and clean:
#Repository
public interface MessageRepository extends JpaRepository<Message, Long> {
Optional<Message> findByMessageId(Long id);
#Query("select m from Message m where date >= :threeDaysAgoDate")
List<Message> findAllWithDateAfter(#Param("threeDaysAgoDate") LocalDate threeDaysAgoDate);
}

Hibernate: How to display data from multiple table

I am new in spring/hibernate technologies, I have tried to find an information about it, but failed, so if you can help I will be so thankful!
I need to display a JSON response in browser of multiple tables, one of the table has primary key for another one.
My entities:
#Entity
#Table
#ToString
public class Book {
#Id
#GeneratedValue(strategy = AUTO)
#JsonView(Views.IdName.class)
private Long book_id;
#JsonView(Views.IdName.class)
private String name;
#Column(length = 1000000)
private String text;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="author_id")
#JsonView(Views.IdName.class)
private Author author;
// ....get/set methods
Another one:
#Entity
#Table
#ToString
public class Page {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
#Column(length = 1000000)
private String text;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "book_id")
private Book book;
// ...get/set methods
My controllers:
#RestController
#RequestMapping("books")
public class BookController {
private final BookRepo bookRepo;
#Autowired
public BookController(BookRepo bookRepo) {
this.bookRepo = bookRepo;
}
#GetMapping
#JsonView(Views.IdName.class)
public List<Book> getAll() {
return bookRepo.findAll();
}
#GetMapping("{id}")
public Book getOne(#PathVariable("id") Book book) {
return book;
}
}
Another one:
#RestController
#RequestMapping("authors")
public class AuthorController {
private final AuthorRepo authorRepo;
#Autowired
public AuthorController(AuthorRepo authorRepo) {
this.authorRepo = authorRepo;
}
#GetMapping
public List<Author> getAll() {
return authorRepo.findAll();
}
#GetMapping("{id}")
public Optional<Author> getOne(#PathVariable("id") Long id) {
return authorRepo.findById(id);
}
}
And also repo for interaction with DB (they are the similar):
public interface AuthorRepo extends JpaRepository<Author, Long> {
}
So when I make a request for get all books, I take the following JSON:
enter image description here
Bit I want different result, something like:
[
{
"book_id" : 1,
"name": "name 1 book",
"author" :
{
"author_id" : 1,
"name": "some_name"
}
}
]
Also, when I tried to make a request for /authors/1, I will get the following response (something like recursion) :
enter image description here
So any help how can I handle with it? Thank you!
You can use a #NoRepositoryBean
like in this example:
#NoRepositoryBean
public interface MappedTypeRepository<T extends AbstractMappedType>
extends Repository<T, Long> {
#Query("select new com.example.YourObjectWithConstructor(e.attribute, sub.sub_attribute) from entity e inner join e.subtable sub where e.attribute = ?1")
List<YourObjectWithConstructor> findAllByAttribute(String attribute);
}
My example may not be 100% correct, I did not check the syntax. Feel free to explore it
Check this also:
JPQL Create new Object In Select Statement - avoid or embrace?

Spring boot MongoDb complex query

I have been learning myself MongoDB implementation in Spring Boot.
However, I came into a problem with complex queries.
I cannot find any right solution for how to implement complex queries to MongoDB from Spring boot.
I am querying the database with MongoRepository interface implementation.
Let's say that I have three collections:
Person - 1 Person can have many Pets.
Pet - 1 Pet can have 1 PetToy and 1 Person who owns him.
PetToy - 1 PetToy can belong to 1 Pet.
POJO classes are bellow
What do I want to achieve?
I want to make a query, which would be returned me a Person, whose Pet has a Toy (PetToy) with the name "Teddy".
I could not have found a way how to do it. Furthermore, is it the best practice to even use such complex queries, or is it better to write more of little ones in MongoDB?
POJOs:
#Document
#Data
#ToString
public class Person {
#Id
private String id;
private String firstname;
private String lastname;
private int age;
#DBRef
private Pet pet;
}
#Document
#Data
#ToString
public class Pet {
#Id
private String id;
private String name;
private int age;
#DBRef
private List<PetToy> toys;
}
#Document
#Data
#ToString
public class PetToy {
#Id
private String id;
private String name;
}
I have tried to use MongoRepositories; however, I was not able to make the complex query.
How can one write such a query to a MongoDB from Spring Boot?
Thank you very much in advance.
If you can use embedded attributes, the class model should be:
#Document
#Data
#Builder
public class Person {
#Id
private String id;
private String firstName;
private String lastName;
private int age;
private List<Pet> pets;
}
#Data
#Builder
public class Pet {
private String name;
private int age;
private List<PetToy> toys;
}
#Data
#Builder
public class PetToy {
private String name;
}
The repository with the method that achieves what you want:
public interface PersonRepository extends MongoRepository<Person, String> {
List<Person> getByPetsToysName(String name);
}
The getByPetsToysName method basically navigate between Person's attributes Person->pets->toys->name. More info here.
An example
#Configuration
#EnableMongoRepositories
public class TestMongo implements CommandLineRunner {
private final PersonRepository repository;
public TestMongo(PersonRepository repository) {
this.repository = repository;
}
#Override
public void run(String... args) throws Exception {
repository.save(Person.builder()
.firstName("John")
.lastName("Doe")
.age(20)
.pets(Stream.of(Pet.builder()
.name("Ursa")
.age(1)
.toys(Stream.of(PetToy.builder()
.name("Teddy")
.build())
.collect(Collectors.toList()))
.build())
.collect(Collectors.toList()))
.build());
repository.save(Person.builder()
.firstName("Phillip")
.lastName("Larson")
.age(21)
.pets(Stream.of(Pet.builder()
.name("Bella")
.age(5)
.toys(Stream.of(PetToy.builder()
.name("Lolo")
.build())
.collect(Collectors.toList()))
.build())
.collect(Collectors.toList()))
.build());
List<Person> persons = repository.getByPetsToysName("Teddy");
System.out.println(persons.size());
List<Person> persons1 = repository.getByPetsToysName("Lolo");
System.out.println(persons1.size());
}
}
Logs:
find using query: { "pets.toys.name" : "Teddy" } fields: Document{{}} for class: class Person in collection: person
If you want more complex queries you can to take a look at the Spring Data MongoDB docs.

Hibernate Fetch #Formula annotated fields on demand

I have a entity (declared with 2 way)(some not influencing code part are ommited for readability)
Entity version 1.
#Entity
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Formula("(SELECT COUNT(w.id) FROM stock s LEFT JOIN warehouse w ON s.id=w.stock_id WHERE s.article_id = id)")
private int variants;
public int getVariants() {
return variants;
}
}
Entity version 2.
#Entity
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Transient
private int variants;
#Access(AccessType.PROPERTY)
#Formula("(SELECT COUNT(w.id) FROM stock s LEFT JOIN warehouse w ON s.id=w.stock_id WHERE s.article_id = id)")
public int getVariants() {
return variants;
}
}
respective DTO and ArticleMapper - MapStruct
#JsonIgnoreProperties(ignoreUnknown = true)
public class ArticleDTOCommon {
private Long id;
private String name;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class ArticleDTO {
private Long id;
private String name;
private int variants;
}
#Mapper(componentModel = "spring", uses = {})
public interface ArticleMapper{
ArticleDTO toDto(Article article);
ArticleDTOCommon toDtoCommon(Article article);
}
I have a #Service layer on which how i know Hibernate creates it's proxy(for defining which field is fetch or not fetch) and transactions are occurs.
#Service
#Transactional
public class ArticleService {
#Transactional(readOnly = true)
public List<ArticleDTO> findAll() {
return articleRepository.findAll()
stream().map(articleMapper::toDto).collect(Collectors.toList());
}
#Transactional(readOnly = true)
public List<ArticleDTO> findAllCommon() {
return articleRepository.findAll()
stream().map(articleMapper::toDtoCommon).collect(Collectors.toList());
}
}
It works fine with fetching Related entity but
Problem is (fetching #Formula annotated field) when I am looking executed query on log it fetchs all time variants #Formula annotated query not depending on respective DTO.
But it must be ignored on toDtoCommon - i.e. It must not fetch variants field -> because when mapping Article to ArticleDtoCommon it not uses getVariant() field of Article. I have tried multiple ways as mentioned above.
I can solve it with writing native query(for findAllCommon() method) and map respectivelly with other way... But I want to know that how we can solve it with ORM way and where is problem.
Manupulating #Access type is not helping too.
Thanks is advance.

Resources