JPQL query in Spring data jpa - java

Lets say we have :
class Range {
public long start;
public long end;
}
This JPA entity:
#Entity
#Table(name="entry")
public class Entry {
#Id
long id;
#OneToMany
Set<Individual> individuals;
}
another JPA entity:
#Entity
#Table(name="individual")
public class Individual {
#Id
long id;
long code;
#ManyToOne
Entry entry;
}
and a controller:
public class IndexedEntryController {
EntityManagerFactory emf;
List<Entry> find(List<Range> lst) {
String str = "";
for(Range r:lst) {
if(!str.isEmpty) {
str += " or ";
}
str += "(i.code between " + r.start + " and " + r.end + ")";
}
String query = "Select i.entry from Individual i where " + str + " group by i.entry having count(i) > " + lst.size()-1;
EntityManager em = emf.createEntityManager();
return em.createQuery(query).getResultList();
}
}
This query returns all entries that have n-1 individuals with code between one of specific ranges, where n is number of ranges.
I want to move to Spring JpaRepository. How can I implement this query with a JpaRepository?

Related

o.h.engine.jdbc.spi.SqlExceptionHelper: ERROR: Column cliententi0_.name does not exist

Repostory
#Repository
public interface ClientRepository extends JpaRepository<ClientEntity, Long> {
#Modifying
#Transactional
#Query(value = "SELECT pp.id, TO_CHAR(pp.created_dt::date, 'dd.mm.yyyy')\n" +
"AS 'Data', CAST(pp.created_dt AS time(0)) AS 'Time', au.username AS 'UserName',\n" +
"ss.name AS 'Service', pp.amount AS 'Amount',\n" +
"REPLACE(pp.status, 'SUCCESS', 'Success') AS 'Payment_status', pp.account AS 'Account',\n" +
"pp.external_id AS 'Idn', COALESCE(pp.external_status, null, 'DN')\n" +
"AS 'Stat'\n" +
"FROM payments AS pp\n" +
"INNER JOIN user AS au ON au.id = pp.creator_id\n" +
"INNER JOIN services AS ss ON ss.id = pp.service_id\n" +
"WHERE pp.created_dt >= '2021-09-28'\n" +
"AND ss.name = 'Faberlic' AND pp.status = 'SUCCESS'", nativeQuery = true)
List<Client> getAllByRegDate();
}
Inteface
public interface Client {
Long getId();
#JsonFormat(shape = JsonFormat.Shape.STRING)
LocalDate getCreated_dt();
String getUsername();
String getName();
int getAmount();
String getStatus();
String getAccount();
String getExternal_id();
String getExternal_status();
}
DTO
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class ClientDto {
private Long id;
#JsonFormat(shape = JsonFormat.Shape.STRING)
private LocalDate created_dt;
private String username;
private String name;
private int amount;
private String status;
private String account;
private String external_id;
private String external_status;
public ClientDto(Client client) {
this.id = client.getId();
/...
/...
this.external_status = client.getExternal_status();
}
public ClientDto(ClientDto clientDto) {
this.id = clientDto.getId();
/...
this.external_status = clientDto.getExternal_status();
}
public ClientDto(ClientEntity clientEntity) {
}
#Override
public String toString() {
return "" + id + "|" + created_dt + "|" + username + "|" + name +
"|" + amount + "|" + status + "|" + account + "|" + external_id + "|" + external_status;
}
}
Entity
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Immutable
#Entity
#Table(name = "payments", schema = "public")
public class ClientEntity {
#Id
private Long id;
#Column(name = "created_dt")
private LocalDate created_dt;
#Column(name = "username")
private String username;
#Column(name = "name")
private String name;
#Column(name = "amount")
private int amount;
#Column(name = "status")
private String status;
#Column(name = "account")
private String account;
#Column(name = "external_id")
private String external_id;
#Column(name = "external_status")
private String external_status;
}
I am trying to save data to a csv file. I take data from one database, from three tables. In entity #Table in "name" I specify one of the existing tables - "payment". All data is taken from three tables (as I have written in Query). But when program is run, an error appears that the "name" column does not exist. This column is in another table from which I am fetching data. Can't figure out what I should do.
This is more of an answer to this question and the question you asked here, combined. Imho you are making things overly complex with your structure of having a Client interface which is used as a projection, which is then turned into a ClientDto (why? the projection is already a DTO) and you have your entities.
Instead of doing this just use a JdbcTemplate with a RowCallbackHandler to write the rows to CSV. This will use a lot less memory, be faster (as you aren't creating multiple objects per row to then throw it away, and you don't have all the rows in memory).
import java.io.FileWriter;
import java.sql.ResultSet;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
public class SchedulerService {
private static final String QUERY = "SELECT pp.id, pp.created_dt au.username, ss.name, pp.amount\n" +
"REPLACE(pp.status, 'SUCCESS', 'Success'), pp.account,\n" +
"pp.external_id AS 'Idn', COALESCE(pp.external_status, null, 'DN') AS 'Stat'\n" +
"FROM payments AS pp\n" +
"INNER JOIN user AS au ON au.id = pp.creator_id\n" +
"INNER JOIN services AS ss ON ss.id = pp.service_id\n" +
"WHERE pp.created_dt >= '2021-09-28'\n" +
"AND ss.name = 'Faberlic' AND pp.status = 'SUCCESS'";
private static final DateTimeFormatter date_format = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private static final DateTimeFormatter time_format = DateTimeFormatter.ofPattern("HH:mm:ss");
private final JdbcTemplate jdbc;
public SchedulerService(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
#Scheduled(fixedRate = 5000)
public void downloadBlockedClients() {
String filename = "select.csv";
try (FileWriter writer = new FileWriter(filename)) {
writer.append("id|date|time|username|name|amount|status|account|external_id|external_status").append('\n');
this.jdbc.query(QUERY, (ResultSet rs) -> writeLine(writer, rs));
} catch (Exception e) {
e.printStackTrace();
}
}
private void writeLine(FileWriter writer, ResultSet rs) {
try {
LocalDateTime ldt = rs.getTimestamp("created_dt").toLocalDateTime();
writer.append(String.valueOf(rs.getLong("id")));
writer.append('|');
writer.append(ldt.format(date_format));
writer.append('|');
writer.append(ldt.format(time_format));
writer.append('|');
writer.append(rs.getString("username"));
writer.append('|');
writer.append(rs.getString("name"));
writer.append('|');
writer.append(String.valueOf(rs.getBigDecimal("amount")));
writer.append('|');
writer.append(rs.getString("status"));
writer.append('|');
writer.append(rs.getString("account"));
writer.append('|');
writer.append(rs.getString("idn"));
writer.append('|');
writer.append(rs.getString("stat"));
writer.append('\n');
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
Something along these lines will make your resources more efficient (saves the copying, having results duplicated in memory) and should be faster. You could move the row handling to a method so your lambda gets a bit more readable.
NOTE: I assumed that you are using Spring Boot and that the `JdbcTemplate is available out-of-the-box. If not you need to configure one next to your JPA configuration.
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}

How do I make a DTO for the Page<> interface?

I am writing an online store using Spring Boot (MVC) and Hiberbate. The problem is that when I get a list of drinks, JSON gives me unnecessary information from the Page interface. I don't know how you can create an DTO for the interfaces to get rid of these fields. What should I do in this situation. Can someone have a ready-made solution?
public Page<DrinkDTO> getAllDrinks(int page, int pageSize) {
PageRequest pageRequest = PageRequest.of(page, pageSize, Sort.by("id"));
final Page<Drink> drinks = drinkRepository.findAll(pageRequest);
return drinkMapper.drinksToDrinksDTO(drinks);
}
#Data
#AllArgsConstructor
public class CustomPage {
Long totalElements;
int totalPages;
int number;
int size;
}
#Data
public class PageDTO<T> {
List<T> content;
CustomPage customPage;
public PageDTO(Page<T> page) {
this.content = page.getContent();
this.customPage = new CustomPage(page.getTotalElements(),
page.getTotalPages(), page.getNumber(), page.getSize());
}
Service for example:
public PageDTO<DrinkDTO> getAllDrinks(int page, int pageSize) {
PageRequest pageRequest = PageRequest.of(page, pageSize, Sort.by("id"));
final Page<Drink> drinks = drinkRepository.findAll(pageRequest);
return new PageDTO<DrinkDTO>(drinkMapper.drinksToDrinksDTO(drinks));
}
I use native query and then I i do dto projections most of the time.
Here is an example of DTO projection
public interface OvertimeRequestView {
public Long getId();
public String getEmployeeFirstName();
public String getEmployeeLastName();
public Long getOvertimeHours();
public Date getOvertimeDate();
public String getDescription();
public String getStatus();
public String getApproverName();
public default String getEmployeeFullName() {
String lastName = this.getEmployeeLastName();
String firstName = this.getEmployeeFirstName();
if (null != firstName) {
return firstName.concat(" ").concat(lastName);
}
return lastName;
}
}
and here is the repository with a native query. notice that since the query returns 'id' column I have a getId() method in the dto above,and since it has employeeFirstName i have getEmployeeFirstName() in the dto and so on. Notice also that I include a count query, without a count query, the queries sometime fail especially if the queries are complex and contain joins
#Query(value = "select ovr.id,\n" +
" u.first_name as employeeFirstName,\n" +
" u.last_name as employeeLastName,\n" +
" ovr.overtime_date as overtimeDate,\n" +
" ovr.description as description,\n" +
" ovr.overtime_hours as overtimeHours\n" +
"from overtime_requests ovr\n" +
" left join employees e on ovr.employee_id = e.id\n" +
" left join users u on e.user_id = u.id",
nativeQuery = true,
countQuery = "select count(ovr.id)\n" +
"from overtime_requests ovr\n" +
" left join employees e on ovr.employee_id = e.id\n" +
" left join users u on e.user_id = u.id")
public Page<OvertimeRequestView> getAllActive(Pageable pageable);
For more you can check from spring data documentation

how to change object to string(java spring boot)

when i run the code i got the object
[[Ljava.lang.Object;#8f17f7c, [Ljava.lang.Object;#6c0a4f24,
[Ljava.lang.Object;#be4886c, [Ljava.lang.Object;#1760591d,
[Ljava.lang.Object;#14e9ce12, [Ljava.lang.Object;#2aa4c0c4,
[Ljava.lang.Object;#5ac9a14]
so.. i want to get the String result which in the below plz teach me the way
[Dataset_info(Ds_id=1111, ds_code=a, ds_name=e, ds_category=g, ds_stru=q, insert_ddtt=null, update_ddtt=null), Dataset_info(Ds_id=11111, ds_code=z, ds_name=eww, ds_category=g, ds_stru=q, insert_ddtt=null, update_ddtt=null)]
#Data
#Entity
#Table(name = "category")
#DynamicInsert
#DynamicUpdate
#NoArgsConstructor
#AllArgsConstructor
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id", columnDefinition = "INT(11)")
private Integer Category_id;
#Column(name = "name", columnDefinition = "VARCHAR(20)")
private String name;
#Column(name = "parent", columnDefinition = "int(11)")
private Integer parent;
}
this is my Category Code
#RestController
#RequestMapping(value = "/Category")
#Slf4j
public class CategoryController {
#Autowired CategoryRepository categoryRepository;
#RequestMapping(value = "/all", method =
RequestMethod.GET)
#ResponseBody
public String getCategoryList() {
List < Object[] > all =
this.categoryRepository.findByCategory();
return all.toString();
//log.info(query);
//return "Test";
}
}
this is my CategoryController code
import java.util.List;
#Repository
public interface CategoryRepository extends
JpaRepository < Category, Integer > {
public static final String FIND_PROJECTS = "SELECT t1.name
AS lev1,
t2.name as lev2,
t3.name as lev3,
t4.name as lev4
FROM category AS t1 LEFT JOIN category AS t2 ON t2.parent =
t1.category_id LEFT JOIN category AS t3 ON t3.parent =
t2.category_id LEFT JOIN category AS t4 ON t4.parent =
t3.category_id WHERE t1.name = 'ROOT'
";
#Query(value = FIND_PROJECTS, nativeQuery = true)
public List < Object[] > findByCategory();
}
this is my CategoryRepository Code
private void mysql2() {
this.categoryRepository.findByCategory();
}
this is my application Code for running
so plz teach me i crave to know the way
thank you
You can use Projection to contain these values :
public interface CategoryProjection {
public String getLev1();
public String getLev2();
public String getLev3();
public String getLev4();
}
Then use the interface Projection with Repository :
//...
#Query(value = FIND_PROJECTS, nativeQuery = true)
public List<CategoryProjection> findByCategory();
How to access values in Projections
Because It's a interface, only have getter method.
Using Loop (foreach loop, fori loop, ...)
ex :
List<CategoryProjection> list = categoryRepository.findByCategory();
list.forEach(c -> {
System.out.println(c.getLev1() + " - " + c.getLev2());
});
// loop i
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getLev1() + " - " + list.get(i).getLev2());
}
Using index
ex : to get Object Category in index 0
String lev1 = list.get(0).getLev1();
UPD: In your case, I think you can change the method return type to List, HttpMessageConverter would convert the result as JSON String to client. Hope it help.
#ResponseBody
public List getCategoryList() {
List<Object[]> all = this.categoryRepository.findByCategory();
return all;
}
Override toString() method in your POJO object. For example:
public class Category {
private Integer Category_id;
private String name;
private Integer parent;
//omitted getter/setter
#Override
public String toString() {
return "Category{" +
"Category_id=" + Category_id +
", name='" + name + '\'' +
", parent=" + parent +
'}';
}
}

using toString on a "child" class where the "parent" is a Hiberneate/JPA class

I am working with REST APIs , Java, Hibernate and JPA.
I have a class Mstrgetnroletype (i.e. role type) that is part of another class called ApplicationUser.
class ApplicationUser
#Entity
#Table(name = "APPLICATIONUSER", schema = "orcl", uniqueConstraints = { #UniqueConstraint(columnNames = "EMAIL"),
#UniqueConstraint(columnNames = "USERNAME") })
public class ApplicationUser implements java.io.Serializable {
private BigDecimal id;
private Mstrgenroletype mstrgenroletype;
private Mstrgenyesno mstrgenyesnoByIsactive;
private Mstrgenyesno mstrgenyesnoByIsstaff;
private Mstrgenyesno mstrgenyesnoByIssuperuser;
[.. snip ..]
class Mstrgenroletype
#Entity
#Table(name = "MSTRGENROLETYPE", schema = "orcl")
public class Mstrgenroletype implements java.io.Serializable {
private BigDecimal mstrgenroletypeid;
private String langid;
private String shortdesc;
private String longdesc;
private Set<Applicationuser> applicationusers = new HashSet<Applicationuser>(0);
public Mstrgenroletype() {
}
public Mstrgenroletype(BigDecimal mstrgenroletypeid) {
this.mstrgenroletypeid = mstrgenroletypeid;
}
public Mstrgenroletype(BigDecimal mstrgenroletypeid, String langid, String shortdesc, String longdesc,
Set<Applicationuser> applicationusers) {
this.mstrgenroletypeid = mstrgenroletypeid;
this.langid = langid;
this.shortdesc = shortdesc;
this.longdesc = longdesc;
this.applicationusers = applicationusers;
}
#Id
#Column(name = "MSTRGENROLETYPEID", unique = true, nullable = false, precision = 22, scale = 0)
public BigDecimal getMstrgenroletypeid() {
return this.mstrgenroletypeid;
}
public void setMstrgenroletypeid(BigDecimal mstrgenroletypeid) {
this.mstrgenroletypeid = mstrgenroletypeid;
}
#Column(name = "LANGID", length = 2)
public String getLangid() {
return this.langid;
}
public void setLangid(String langid) {
this.langid = langid;
}
#Column(name = "SHORTDESC", length = 10)
public String getShortdesc() {
return this.shortdesc;
}
public void setShortdesc(String shortdesc) {
this.shortdesc = shortdesc;
}
#Column(name = "LONGDESC", length = 20)
public String getLongdesc() {
return this.longdesc;
}
public void setLongdesc(String longdesc) {
this.longdesc = longdesc;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "mstrgenroletype")
public Set<Applicationuser> getApplicationusers() {
return this.applicationusers;
}
public void setApplicationusers(Set<Applicationuser> applicationusers) {
this.applicationusers = applicationusers;
}
#Override
public String toString() {
return "Mstrgenroletype [mstrgenroletypeid=" + mstrgenroletypeid + ", langid=" + langid + ", shortdesc="
+ shortdesc + ", longdesc=" + longdesc + "]";
}
}
I am trying to make a method of "toString" that will print out the contents of the variable associated with ApplicationUser.
The problem is that when printing out the contents of the variable associated with Mstrgenroletype, I get an error of
org.hibernate.LazyInitializationException: could not initialize proxy
- no Session
This is because it is trying to print the contents associated with the class of Mstrgenroletype. (I know this because when I remove the print statement assocaited with variable that is linked to the class, everything works)
BEFORE (getting the error)
#Override
public String toString() {
return "ApplicationUser [id=" + id + ", password=" + password + ", username=" + ", role=" + mstrgenroletype.toString()
+ username + ", firstname=" + firstname + ", lastname=" + lastname + ", email=" + email + ", userid="
+ userid + ", apptenantid=" + apptenantid + ", appkeyid=" + appkeyid + ", profilepic=" + profilepic + "]";
}
AFTER (Mstrgenroltype variable removed, getting no error)
#Override
public String toString() {
return "ApplicationUser [id=" + id + ", password=" + password + ", username="
+ userid + ", apptenantid=" + apptenantid + ", appkeyid=" + appkeyid + ", profilepic=" + profilepic + "]";
}
I have made sure that the class with Mstrgenroletype does have a toString method as well.
How can I print the variable associated with Mstrgenroletype?
You invoke the toString() method on a ApplicationUser object that didn't load the mstrgenroletype relationship eagerly.
As ApplicationUser.toString() method invokes the toString() method of the object referenced by mstrgenroletype, it throws an exception because the ApplicationUser object is currently a Hibernate proxy that can load relationship only in the frame of a Hibernate session.
But according to the thrown exception, you don't have any currently opened session during the toString() invocation.
How can I print the variable associated with Mstrgenroletype?
First, you should avoid having a toString() which the results depends on a persistence session.
It mix things. toString() is for debugging an object, not for fetching fields with query executions.
It is an important side effect you have to avoid.
So I advise to write a toString() method that relies only on own fields of the entity as this one you wrote in your workaround.
This rule should also be followed for equals() or hashCode() methods
Of course if functionally you need to retrieve the relationship, add it in your query with a fetch join.
If it is not the case and you want only to debug the object relationships you can add a breakpoint in your IDE in a class where the Hibernate session is opened and the object retrieved and dig into the relationship with IDE tools (display or execute feature in Eclipse).

Neo4J, Spring Data. How to query Relationship entity?

I use Neo4J database with Spring Data. I am unable to query (with custom query) a relationship directly to my Relation entity which looks like that:
#RelationshipEntity(type = "OCCURS_WITH")
public class Relation {
#GraphId
private Long id;
#StartNode
#Fetch
private Hashtag from;
#EndNode
#Fetch
private Hashtag to;
#GraphProperty(propertyType = long.class)
private Long[] timestamps = new Long[0];
private boolean active;
// getters, setters
}
I have also a repository interface as follow:
public interface RelationRepository extends CRUDRepository<Relation> {
#Query(value = " MATCH (h1)-[rel]->(h2) " +
" WHERE h1.name = {0} AND h2.name = {1}" +
" RETURN rel")
Relation find(String from, String to);
}
But when I query the repository I get an empty Relation object.
Everything works well when I am quering to dummy object in that way:
#Query(value = " MATCH (h1)-[r]->(h2) " +
" WHERE h1.name = {0} AND h2.name = {1} " +
" RETURN id(r) AS id, h1.name AS from, h2.name AS to, length(r.timestamps) AS size")
RelationshipData findData(String from, String to);
#QueryResult
public interface RelationshipData {
#ResultColumn("id")
String getId();
#ResultColumn("from")
String getFrom();
#ResultColumn("to")
String getTo();
#ResultColumn("size")
int getSize();
}
Is it possible to query directly to my entity?

Categories

Resources