JPA Criteria select from some tables - java

There are the following structure of tables:
CREATE TABLE Train
(
idTrain INT NOT NULL AUTO_INCREMENT,
nameTrain CHAR(20) NOT NULL,
PRIMARY KEY (idTrain)
);
CREATE TABLE Station
(
idStation INT NOT NULL AUTO_INCREMENT,
nameStation CHAR(50) NOT NULL,
PRIMARY KEY (idStation)
);
CREATE TABLE Schedule
(
idStation INT NOT NULL,
idTrain INT NOT NULL,
arrivalTime TIME NOT NULL,
departureTime TIME NOT NULL,
nextStation INT NOT NULL,
kmToNextStation INT NOT NULL,
PRIMARY KEY (idStation, idTrain, nextStation),
FOREIGN KEY (idStation) REFERENCES Station(idStation),
FOREIGN KEY (idTrain) REFERENCES Train(idTrain),
FOREIGN KEY (nextStation) REFERENCES Station(idStation)
);
Necessary to implement the following sql-query using JPA Criteria:
SELECT Station.nameStation, Schedule.arrivalTime, Schedule.departureTime, Schedule.kmToNextStation
FROM Schedule
JOIN Station
ON Station.idStation = Schedule.idStation
JOIN Train
ON Schedule.idTrain = Train.idTrain
WHERE Train.nameTrain = "268A";
Here is my attempt:
EntityManager em = EntitySupport.getEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<ScheduleEntity> cq = builder.createQuery(ScheduleEntity.class);
Root<ScheduleEntity> root = cq.from(ScheduleEntity.class);
Join<ScheduleEntity, TrainEntity> idTrain = root.join("idTrain");
Join<ScheduleEntity, StationEntity> idStation = root.join("idStation");
cq.multiselect(root.get("arrivalTime"),
root.get("departureTime"),
idTrain.get("nameTrain"));
Query query = em.createQuery(cq);
List res = query.getResultList();
System.out.println("Result query: " + res.toString());
Apparently, I'm doing something wrong, because I get the following error:
Exception in thread "main" java.lang.IllegalArgumentException:
org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate
appropriate constructor on class [srt.entity.ScheduleEntity]. Expected
arguments are: java.util.Date, java.util.Date, java.lang.String
[select new srt.entity.ScheduleEntity(generatedAlias0.arrivalTime,
generatedAlias0.departureTime, generatedAlias1.nameTrain) from
srt.entity.ScheduleEntity as generatedAlias0 inner join
generatedAlias0.idTrain as generatedAlias1 inner join
generatedAlias0.idStation as generatedAlias2]
Help me make the correct code for the above sql-query using JPA Criteria.

I was wrong in the concept of JPA Criteria. Now I completely understood, the answer would be as follows:
EntityManager em = EntitySupport.getEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<ScheduleEntity> cq = builder.createQuery(ScheduleEntity.class);
Root<ScheduleEntity> from = cq.from(ScheduleEntity.class);
Join<ScheduleEntity, StationEntity> idStation = from.join("idStation");
Join<ScheduleEntity, TrainEntity> idTrain = from.join("idTrain");
Predicate where = builder.equal(idTrain.get("nameTrain"), "268A");
cq.where(where);
List<ScheduleEntity> schedule = em.createQuery(cq).getResultList();
for(ScheduleEntity s : schedule) {
System.out.println(s.toString());
}

Related

Get only the latest status by MAX function for each Application using WHERE clausule instead of ORDER BY

I would like to obtain the same result of query using WHERE instead of GROUP BY. I have applications and each application has statuses with date. I need to return applications with their latest status date.
My query with ORDER BY: (the result is correct)
select a.guid, MAX(s.request_time) as last_request_time
from public.applications_status s inner join public.applications a on a.guid = s.guid_application
group by a.guid ;
result:
guid |last_request_time |
------------------------------------|-------------------|
330d32d5-2496-4cce-9d11-29e59333766a|2020-07-22 13:06:25|
5b46cda9-b954-4d8b-82cf-f1d83f77b175|2020-07-22 13:07:25|
34071189-ab3d-47ff-9ee1-aca6fa806bc9|2020-08-03 10:45:15|
a8961058-a6ee-4d71-b325-9aca83b22237|2020-08-03 10:45:39|
ff98695f-e1a8-439e-8a6c-7991348b6cd7|2020-07-29 14:38:18|
I try this but it return me only the one application with latest status date:
select a.guid, s.request_time
from public.applications_status s inner join public.applications a on a.guid = s.guid_application
where request_time = (select MAX(applications_status.request_time) from applications_status );
result:
guid |request_time |
------------------------------------|-------------------|
a8961058-a6ee-4d71-b325-9aca83b22237|2020-08-03 10:45:39|
Applications table
CREATE TABLE public.applications (
id bigserial NOT NULL,
guid varchar(40) NOT NULL,
"name" varchar(60) NOT NULL,
latest_status_date timestamp NULL,
latest_status bool NOT NULL,
id_production bigserial NOT NULL,
CONSTRAINT applications_guid_key UNIQUE (guid),
CONSTRAINT applications_pkey PRIMARY KEY (id),
CONSTRAINT uk_gtuqgycxk8ulkir3io2p49yn1 UNIQUE (guid),
CONSTRAINT fkaid_prod FOREIGN KEY (id_production) REFERENCES productions(id) ON DELETE CASCADE
);
Applications_status table
CREATE TABLE public.applications_status (
id bigserial NOT NULL,
status bool NOT NULL,
guid_application varchar(50) NOT NULL,
log varchar(200) NULL,
request_time timestamp NOT NULL,
CONSTRAINT status_pkey PRIMARY KEY (id),
CONSTRAINT fkaguid_application FOREIGN KEY (guid_application) REFERENCES applications(guid) ON UPDATE CASCADE ON DELETE CASCADE
);
Why I need this way? I try to return Applications with their latest status in Spring Boot using #Where annotation in #OneToMany relation in Entity.
#OneToMany(cascade = CascadeType.ALL, mappedBy = "application", fetch = LAZY)
#JsonManagedReference
#Where(clause = "request_time = (SELECT MAX(applications_status.request_time) FROM applications_status )")
#OrderBy("requestTime DESC")
private List<ApplicationStatus> applicationStatuses;
I also try to use #BatchSize(size = 1) but it doesn't work.
The question is tagged both "sql" and "postgres", so this is a Postgres solution.
Use distinct on:
select distinct on (a.guid) a.*, s.*
from public.applications_status s inner join
public.applications a
on a.guid = s.guid_application
order by a.guid, s.request_time desc;
distinct on is a very handy Postgres extension that returns one row (the "first" row) for each group in the parentheses. The particular row is based on the order by.
Through trial and error, I found a solution:
SQL query:
select a.guid, s.request_time
from public.applications_status s inner join public.applications a on a.guid = s.guid_application
where request_time = (select MAX(applications_status.request_time)
from applications_status
where applications_status.guid_application = s.guid_application );
Spring-Boot:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "application", fetch = LAZY)
#JsonManagedReference
#Where(clause = "request_time = (SELECT MAX(applications_status.request_time) FROM applications_status where guid_application = applications_status.guid_application )")
#OrderBy("requestTime DESC")
private List<ApplicationStatus> applicationStatuses;

Not getting MySQL SELECT LPAD() in ResultSet

I am trying to select multiple columns from different tables in MySQL.
MySQL:
CREATE TABLE IF NOT EXISTS movie(
movie_id TINYINT(3) UNSIGNED AUTO_INCREMENT,
movie_title VARCHAR(255) NOT NULL,
movie_genre VARCHAR(255) NOT NULL,
movie_rating ENUM('G', 'PG', 'R-13', 'R-16', 'R-18', 'PENDING') NOT NULL,
movie_cast VARCHAR(255) NOT NULL,
movie_runtime TINYINT(3) UNSIGNED NOT NULL,
movie_poster VARCHAR(255) NOT NULL,
PRIMARY KEY(movie_id)
);
CREATE TABLE IF NOT EXISTS mall(
mall_id VARCHAR(255),
mall_name VARCHAR(255) NOT NULL,
PRIMARY KEY(mall_id)
);
CREATE TABLE IF NOT EXISTS schedule(
schedule_id TINYINT(3) UNSIGNED AUTO_INCREMENT,
movie_id TINYINT(3) UNSIGNED NOT NULL,
mall_id VARCHAR(255) NOT NULL,
schedule_cinema TINYINT(2) UNSIGNED NOT NULL,
schedule_price DECIMAL(5, 2) NOT NULL,
schedule_date DATE NOT NULL,
schedule_time TIME NOT NULL,
schedule_seats TINYINT(2) UNSIGNED NOT NULL,
PRIMARY KEY(schedule_id),
FOREIGN KEY(movie_id) REFERENCES movie(movie_id),
FOREIGN KEY(mall_id) REFERENCES mall(mall_id)
ON DELETE CASCADE
ON UPDATE CASCADE
)ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS transaction(
transaction_id SMALLINT(5) UNSIGNED AUTO_INCREMENT,
user_id VARCHAR(255) NOT NULL,
schedule_id TINYINT(3) UNSIGNED NOT NULL,
transaction_date DATE NOT NULL,
PRIMARY KEY(transaction_id),
FOREIGN KEY(user_id) REFERENCES user(user_id),
FOREIGN KEY(schedule_id) REFERENCES schedule(schedule_id)
ON DELETE CASCADE
ON UPDATE CASCADE
)ENGINE=INNODB;
I tested this query directly in XAMPP MySQL and it returned all desired columns. The transaction_id is also left-padded as intended.
SELECT SQL:
SELECT LPAD(transaction_id, 5, 0), transaction_date, movie_title, schedule_price, mall_name, schedule_cinema, schedule_date, schedule_time FROM transaction INNER JOIN schedule ON transaction.schedule_id = schedule.schedule_id INNER JOIN movie ON schedule.movie_id = movie.movie_id INNER JOIN mall ON schedule.mall_id = mall.mall_id WHERE user_id = 'admin';
This method was supposed to return a list of History objects, using the above SELECT SQL.
public List<History> getTransactionHistory(String currentUserId){
History history;
List<History> historyList = new ArrayList<History>();
sql = "SELECT LPAD(transaction_id, 5, 0), transaction_date, movie_title, schedule_price, "
+ "mall_name, schedule_cinema, schedule_date, schedule_time FROM transaction "
+ "INNER JOIN schedule ON transaction.schedule_id = schedule.schedule_id "
+ "INNER JOIN movie ON schedule.movie_id = movie.movie_id "
+ "INNER JOIN mall ON schedule.mall_id = mall.mall_id "
+ "WHERE user_id = ?";
try {
ps = conn.prepareStatement(sql);
ps.setString(1, currentUserId);
rs = ps.executeQuery();
while(rs.next()) {
history = HistoryAssembler.getInstance(
rs.getString("transaction_id"),
rs.getDate("schedule_date"),
rs.getString("movie_title"),
rs.getBigDecimal("schedule_price"),
rs.getString("mall_name"),
rs.getInt("schedule_cinema"),
rs.getDate("schedule_date"),
rs.getTime("schedule_time")
);
System.out.println(history.getTransactionId());
historyList.add(history);
}
} catch(SQLException se) {
se.printStackTrace();
}
return historyList;
}
History (Bean):
public class History {
private String transactionId;
private Date transactionDate;
private String movieTitle;
private BigDecimal schedulePrice;
private String mallName;
private Integer scheduleCinema;
private Date scheduleDate;
private Time scheduleTime;
// getters and setters
}
HistoryAssembler:
public static History getInstance(String transactionId, Date transactionDate, String movieTitle, BigDecimal schedulePrice,
String mallName, Integer scheduleCinema, Date scheduleDate, Time scheduleTime) {
History history = new History();
history.setTransactionId(transactionId);
history.setTransactionDate(transactionDate);
history.setMovieTitle(movieTitle);
history.setSchedulePrice(schedulePrice);
history.setMallName(mallName);
history.setScheduleCinema(scheduleCinema);
history.setScheduleDate(scheduleDate);
history.setScheduleTime(scheduleTime);
return history;
}
However, I am getting a java.sql.SQLException: Column 'transaction_id' not found, when I do have the said column.
From what I understand, LPAD() should return a String, so that is why I set the transactionId in the bean as such.
Your assistance is much appreciated.
After the "LPAD" function the column name is changed and "XAMPP" fixed for you. You must put alias in this query after "LPAD(transaction_id, 5, 0) as transaction_id".
SELECT LPAD(transaction_id, 5, 0) as transaction_id
, transaction_date
, movie_title
, schedule_price
, mall_name
, schedule_cinema
, schedule_date
, schedule_time
FROM transaction INNER JOIN schedule ON transaction.schedule_id = schedule.schedule_id
INNER JOIN movie ON schedule.movie_id = movie.movie_id
INNER JOIN mall ON schedule.mall_id = mall.mall_id
WHERE user_id = 'admin';

Why is CriteriaBuilder creating a malformed query for "in" CollectionTable?

When attempting to generate dynamic queries using CriteriaBuilder, Hibernate is not creating the proper SQL with regards to an Entities member variable associated with #ElementCollection.
Sample Entity:
#Entity
#Table(name = "myclass")
public class MyClass {
...
#ElementCollection
#CollectionTable(
name = "myclass_mysubclass",
joinColumns = #JoinColumn(name = "myclass_id")
)
#Column(name = "mysubclass_id")
private List<Integer> mySubClassIDs;
...
}
CriteriaBuilder code:
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(MyClass.class);
Root<T> root = criteriaQuery.from(MyClass.class);
Expression<Object> e = root.get("mySubClassIDs");
List<Object> o = (List<Object>) entry.getValue();
criteriaQuery.where(e.in(o));
where entry.getValue() will return an ArrayList<Integer> of [1]
Produces:
SELECT distinct count(myclass0_.id) as col_0_0_
FROM hotel myclass0_
cross join myclass_mysubclass mySubClassids1_
where myclass0_.id=mySubClassids1_.myclass_id and (. in (1))
Why is Hibernate not generating the "in" clause properly? the "." should be mySubClassids1_.mysubclass_id
Am I missing something in the annotation of the member variable? Doesn't seem so, as it is enough to generate the cross join.
The env is Jboss AS 7 with Hibernate 4.2.7.SP1-redhat-3 on jdk-6
Your schema is creating two separate tables:
create table myclass (
id int8 not null,
primary key (id)
);
create table myclass_mysubclass (
myclass_id int8 not null,
mysubclass_id int4
);
So, it seems you need to do a join instead of a get:
Expression<Object> e = root.join("mySubClassIDs");
Worked for me at any rate.

java.sql.SQLException: Subquery returns more than 1 row

I have a program that when executed it gets lots of words from a file and inserts them into a database, after being inserted if the word is inserted twice it calculates the "IDF" again using a trigger. The problem is that if I do this directly into MySQL it has no problem, but if I do this on Java it returns this error:
Exception in thread "main" java.sql.SQLException: Subquery returns more than 1 row
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1086)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4169)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2617)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2828)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2777)
at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:949)
at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:795)
at model.Consultas.altaBajaCambio(Consultas.java:29)
at model.DatosBD.agregarPalabra(DatosBD.java:23)
at search.Search.main(Search.java:36)
Java Result: 1
I assume the problem has to be with the st.execute(), since it only gives back one int, but I have search on the web for a solution and I cannot find one.
Query:
String query2 = "INSERT IGNORE INTO Search.IndiceInv (DocID, Term, TF) VALUES ("+doc+",'"+term+"',1) ON DUPLICATE KEY UPDATE `TF` = `TF` + 1;";
c.altaBajaCambio(query2);
Execution:
try (Connection con = c.getConnection()) {
if (con == null) {
System.out.println("No hay conexion");
} else {
Statement st = con.createStatement();
st.execute(query);
}
Database:
-- -----------------------------------------------------
-- Table `Search`.`Doc`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Search`.`Doc` (
`DocID` INT NOT NULL,
PRIMARY KEY (`DocID`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `Search`.`Term`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Search`.`Term` (
`Term` VARCHAR(45) NOT NULL,
`IDF` INT NOT NULL,
PRIMARY KEY (`Term`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `Search`.`IndiceInv`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Search`.`IndiceInv` (
`DocID` INT NOT NULL,
`Term` VARCHAR(45) NOT NULL,
`TF` INT NOT NULL,
PRIMARY KEY (`DocID`, `Term`),
ENGINE = InnoDB;
Trigger:
delimiter //
create trigger IDF
after update on IndiceInv
for each row
begin
update Term
set IDF = (SELECT LOG((SELECT count(*) FROM Doc)/(SELECT count(DocID) FROM IndiceInv WHERE Term = new.Term)) FROM Doc, IndiceInv)
where Term = new.Term;
end;//
delimiter ;
Try to run manually:
SELECT LOG((SELECT count(*) FROM Doc)/(SELECT count(DocID) FROM IndiceInv WHERE Term = new.Term)) FROM Doc, IndiceInv
(assign the relevant values to new.Term etc)

JPA - Retrieving data from database table wrt Foreign key -

I am trying to retrieve data from a table which has a foreign key, "reg_no". And the foreign key is not unique, it can be duplicated.
Now I want to retrieve the data from this table using this foreign key. I will provide a "reg_no" and the Java Persistence API will retrieve a list of the result set from the table wrt to "reg_no" provided.
Please enlighten me how can I solve this problem?
You can do something like this, using JPQL:
String queryString = "SELECT t FROM YourTable t " +
"WHERE reg_no = :regNo";
Query query = getEntityManager().createQuery(queryString);
query.setParameter("regNo", regNoValue);
return query.getResultList();
EntityManager entityManager = entityManagerFactory.createEntityManager();
List results = em.createQuery("SELECT c FROM Vehicle v WHERE reg_no = :reg_no").setParameter("reg_no", new String("0000")).getResultList;

Categories