Neo4J, Spring Data. How to query Relationship entity? - java

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?

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);
}

Read data saved by spark redis using Java

I using spark-redis to save Dataset to Redis.
Then I read this data by using Spring data redis:
This object I save to redis:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Builder
#RedisHash("collaborative_filtering")
public class RatingResult implements Serializable {
private static final long serialVersionUID = 8755574422193819444L;
#Id
private String id;
#Indexed
private int user;
#Indexed
private String product;
private double productN;
private double rating;
private float prediction;
public static RatingResult convert(Row row) {
int user = row.getAs("user");
String product = row.getAs("product");
double productN = row.getAs("productN");
double rating = row.getAs("rating");
float prediction = row.getAs("prediction");
String id = user + product;
return RatingResult.builder().id(id).user(user).product(product).productN(productN).rating(rating)
.prediction(prediction).build();
}
}
Save object by using spark-redis:
JavaRDD<RatingResult> result = ...
...
sparkSession.createDataFrame(result, RatingResult.class).write().format("org.apache.spark.sql.redis")
.option("table", "collaborative_filtering").mode(SaveMode.Overwrite).save();
Repository:
#Repository
public interface RatingResultRepository extends JpaRepository<RatingResult, String> {
}
I can't read this data have been saved in Redis by using Spring data redis because structure data saved by spark-redis and spring data redis not same (I checked value of keys created by spark-redis and spring data redis are different by using command: redis-cli -p 6379 keys \* and redis-cli hgetall $key)
So how to read this data have been saved using Java or by any library in Java?
The following works for me.
Writing data from spark-redis.
I use Scala here, but it's essentially the same as you do in Java. The only thing I changed is I added a .option("key.column", "id") to specify the hash id.
val ratingResult = new RatingResult("1", 1, "product1", 2.0, 3.0, 4)
val result: JavaRDD[RatingResult] = spark.sparkContext.parallelize(Seq(ratingResult)).toJavaRDD()
spark
.createDataFrame(result, classOf[RatingResult])
.write
.format("org.apache.spark.sql.redis")
.option("key.column", "id")
.option("table", "collaborative_filtering")
.mode(SaveMode.Overwrite)
.save()
In spring-data-redis I have the following:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Builder
#RedisHash("collaborative_filtering")
public class RatingResult implements Serializable {
private static final long serialVersionUID = 8755574422193819444L;
#Id
private String id;
#Indexed
private int user;
#Indexed
private String product;
private double productN;
private double rating;
private float prediction;
#Override
public String toString() {
return "RatingResult{" +
"id='" + id + '\'' +
", user=" + user +
", product='" + product + '\'' +
", productN=" + productN +
", rating=" + rating +
", prediction=" + prediction +
'}';
}
}
I use CrudRepository instead of JPA:
#Repository
public interface RatingResultRepository extends CrudRepository<RatingResult, String> {
}
Querying:
RatingResult found = ratingResultRepository.findById("1").get();
System.out.println("found = " + found);
The output:
found = RatingResult{id='null', user=1, product='product1', productN=2.0, rating=3.0, prediction=4.0}
You may notice that the id field was not populated because the spark-redis stored has a hash id and not as a hash attribute.

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 preserve the order of objects in a Java collection?

I have following method which works fine however returns the objects in the alphabetical orders whenever being called from calling method :-
public List<Object> getExportDataFromView() {
try {
String selectQuery = "SELECT UNIQUE_ID, DATE, CODE, PRODUCT_CODE, "
+ " MESSAGE AS MESSAGE_TEXT, SOURCE, COMPANY_ID, REMARK, BATCH, COUNTRY, PRODUCT_CODE_SCHEME, EXTERNAL_TRANSACTION_ID, RETURN_CODE, CORRELATION_ID, BUSINESS_PROCESS, BATCH_EXPIRY_DATE "
+ " FROM \"test\".\"my-systems.Company::views.Export_Data_View\" "
+ " WHERE COMPANY_ID = 'C100' "
+ " ORDER BY DATE DESC";
Query q = this.em.createNativeQuery(selectQuery, ExportData.class);
return q.getResultList();
} catch (Exception e) {
return null;
} finally {
this.em.close();
}
}
Whenever this method is called from another calling method, the List returned is in sorted order (alphabetically). However, in the JPA Entity class of ExportData.java, the fields are declared in original order like below :-
#Entity
#Table(name = "\"my-systems.Company::views.Export_Data_View\"", schema="\"test\"")
public class ExportData implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String UNIQUE_ID;
private String CODE;
private String PRODUCT_CODE;
private String MESSAGE_TEXT;
private String SOURCE;
Is there any way to preserve the same order as declared in Entity class rather than being sorted without changing the return type i.e. List.
Any help would be highly appreciated.
Thank you

How to provide values from a class which is not an entity to a repository in spring data

I have a doubt about Spring Data and Spring Repositories.
I need to provide some values to a CrudRepository from another class which is not an Entity. For example:
I Have a class
#Entity
class Profile {
private String id;
private String name;
private long birthDate;
private String aboutMe;
...
}
and
class MyProfile {
private String profileId;
private String accountId;
private String name;
private String aboutMe;
}
and a Repository
#Transactional
#EnableTransactionManagement
#Repository
public interface ProfileRepository extends CrudRepository<Profile, String>{
#Transactional
Profile findByAccountId(String id);
void updateMyProfile(MyProfile myProfile);
}
and I would like to update only some fields from Profile using data provided in MyProfile. There is a way to do this?
Thanks!!
You will have to write the update query manually:
#Query("UPDATE Profile p" +
" SET " +
" p.name = ?#{#myprofile.name}, " +
" p.aboutMe = ?#{#myprofile.aboutMe}" +
" p.account.id = ?#{#myprofile.accountId}" + // I assume that account is another entity
" WHERE p.id = ?#{#myprofile.profileId}")
#Transactional
#Modifying
void updateMyProfile(#Param("myprofile") MyProfile myprofile);

Categories

Resources