I am working on a Spring application that use JdbcTemplate to query the database, and the result from the rowmapper is different from the result of the query.
My query returns :
+--------------------+---------+------+------+----------+----------+----------+----------+---------+----------------+-----------------+
| ORIGINATING_SYSTEM | SYS_REF | CUR1 | CUR2 | TRADE_DT | START_DT | END_DT | BOOK_REF | BOOK_ID | NOMINAL | ORIGIN_VALUE_DT |
+--------------------+---------+------+------+----------+----------+----------+----------+---------+----------------+-----------------+
| CC | 4000000 | USD | | 01/04/19 | 01/04/19 | 01/04/19 | TDCZK | 317 | -8245872154,55 | 29/03/19 |
| GPS | 4000000 | EUR | | 01/04/19 | 28/03/19 | 28/03/19 | TDCZK | 317 | 55555550 | |
+--------------------+---------+------+------+----------+----------+----------+----------+---------+----------------+-----------------+
This result is processed with a rowmapper to get a list :
public List<Trade> findBackValueTrades() {
List<Trade> trades = getJdbcTemplate().query(FIND_BACK_VALUE_TRADES, new BackValueTradeMapper());
}
class BackValueTradeMapper implements RowMapper<Trade> {
public BackValueTradeMapper() {
}
#Override
public Trade mapRow(final ResultSet rs, final int rowNum) throws SQLException {
Trade trade = new Trade();
trade.setOriginatingSystem(rs.getString("ORIGINATING_SYSTEM"));
trade.setSystemRef(rs.getString("SYS_REF"));
trade.setNominal(rs.getDouble("NOMINAL"));
Currency cur1 = new Currency();
cur1.setId(rs.getString("CUR1"));
trade.setCurrency1(cur1);
Currency cur2 = new Currency();
cur2.setId(rs.getString("CUR2"));
trade.setCurrency2(cur2);
trade.setTradeDate(rs.getDate("TRADE_DT"));
trade.setStartDate(rs.getDate("START_DT"));
trade.setEndDate(rs.getDate("END_DT"));
Book book = new Book();
book.setBookRef(rs.getString("BOOK_REF"));
book.setId(rs.getLong("BOOK_ID"));
trade.setBook(book);
trade.setNominal(rs.getDouble("NOMINAL"));
trade.setEnteredDate(rs.getDate("ORIGIN_VALUE_DT"));
return trade;
}
}
where Trade is just an #Entity, containing only fields and getters and setters :
#Table(name = "TRADES")
#Entity
public class Trade implements Serializable {
private static final long serialVersionUID = 2143115773381859155L;
#Column(name = "ID")
#Id
private Long id;
#Column(name = "SYS_REF")
private String systemRef;
#Column(name = "ORIGINATING_SYSTEM")
private String originatingSystem;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CUR1")
private Currency cur1;
// remaining fields + getters/setters
}
The problem is in the resulting list, where it seems the trade from CC was overwritten :
+--------------------+---------+------+------+----------+----------+----------+----------+---------+-----------+-----------------+
| ORIGINATING_SYSTEM | SYS_REF | CUR1 | CUR2 | TRADE_DT | START_DT | END_DT | BOOK_REF | BOOK_ID | NOMINAL | ORIGIN_VALUE_DT |
+--------------------+---------+------+------+----------+----------+----------+----------+---------+-----------+-----------------+
| GPS | 4000000 | EUR | | 01/04/19 | 28/03/19 | 28/03/19 | TDCZK | 317 | 55555550 | 29/03/19 |
| GPS | 4000000 | EUR | | 01/04/19 | 28/03/19 | 28/03/19 | TDCZK | 317 | 55555550 | 28/03/19 |
+--------------------+---------+------+------+----------+----------+----------+----------+---------+-----------+-----------------+
Why is that ?
By the way, I was able to resolve the issue by extending the Trade class and overriding equals and hashCode, but I want to know why it worked.
Related
I have 3 tables and a ManyToMany relationship between usuario and projeto, and I'm having a headche to make it search accordly :
usuario
+-------------+-------------+
| idMatricula | nomeUsuario |
+-------------+-------------+
| 1 | Victor |
| 12345 | Juquinha |
| 2 | Nilton |
| 3 | Fatima |
| 4 | Bianca |
| 4422 | Pedrinho |
+-------------+-------------+
projeto
+-----------+----------------+--------------+
| idprojeto | nomeProjeto | faseProjeto |
+-----------+----------------+--------------+
| 1 | APS_6_semestre | Em Andamento |
| 2 | APS_7_semestre | Em testes |
| 3 | APS_8_semestre | Em testes |
| 4 | TCC | Concluido |
+-----------+----------------+--------------+
projeto_has_usuario
+-------------------+---------------------+
| projeto_idprojeto | usuario_idMatricula |
+-------------------+---------------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 2 |
+-------------------+---------------------+
I'm trying to query only the projects associated to the user, but my query doesnt work and I dont know if it is a semantic error or an annotation error. Here is my mapped class:
Projeto:
#Entity
#Table(name="projeto")
public class Projeto {
#GeneratedValue
#Id
#Column(name="idprojeto", nullable = false)
private int idprojeto;
#Column(name="nomeProjeto", nullable = false)
private String nomeProjeto;
#Column(name="faseProjeto", nullable = false)
private String faseProjeto;
#ManyToMany(mappedBy="projetos")
private List<Usuario> usuarios;
Usuario:
#Entity
#Table(name="usuario")
public class Usuario {
#Id
#Column(name="idMatricula", nullable = false)
private String id;
#Column(name="nomeUsuario", nullable = false)
private String nomeUsuario;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="projeto_has_usuario",
joinColumns={#JoinColumn(name="usuario_idMatricula")},
inverseJoinColumns={#JoinColumn(name="projeto_idprojeto")})
private List<Projeto> projetos;
Here is my query:
TypedQuery<Usuario> listaUsuario = manager.createQuery("SELECT u FROM Usuario as u", Usuario.class);
// Some validation ..
TypedQuery<Projeto> listaTabela = manager.createQuery("SELECT p FROM projeto p "
+ "JOIN p.usuarios u WHERE p.idprojeto MEMBER OF u.projetos", Projeto.class);
for (Projeto projetos : listaTabela.getResultList()) {
System.out.println("Nome: " + projetos.getNomeProjeto() + "\nID: " + projetos.getIdprojeto()
+ "\nFase: " + projetos.getFaseProjeto());
Returns me:
Nome: APS_6_semestre
ID: 1
Fase: Em Andamento
I'm using Eclipse IDE 4.7.2, Hibernate 5.2.12 and JPA 2.1.
The environment is Java, Spring-boot, Hibernat, QueryDSL, MySQL.
I have table structure
Episode
+----+-------------+--------
| id | address_id | eventno
+----+-------------+--------
| 5 | 27 | F123
| 6 | 30 | F456
| 7 | 45 | F789
+----+-------------+--------
#Entity
public class Episode {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty
private String eventno;
#ManyToOne(cascade = CascadeType.ALL)
private Address address;
Episode_Person
+----+--------------+--------------+------------+-----------+
| id | episode_role | primary_flag | episode_id | person_id |
+----+--------------+--------------+------------+-----------+
| 19 | Buyer | | 5 | 1 |
| 20 | Subject | | 5 | 2 |
| 23 | Witness | | 6 | 3 |
| 24 | Child | | 6 | 4 |
| 27 | Buyer | | 5 | 3 |
| 63 | Investor | | 5 | 4 |
| 64 | Subject | | 7 | 1 |
| 65 | Subject | | 7 | 3 |
#Entity
public class EpisodePerson {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#Valid
private Person person;
#ManyToOne
private Episode episode;
Person
+----+-----------+----------+
| id | firstname | surname |
+----+-----------+----------+
| 1 | Clint | eastwood |
| 2 | Angelina | joilee |
| 3 | Brad | pitt |
| 4 | Jennifer | aniston |
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = {"nia"}))
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String surname;
private String firstname;
private String gender;
So each episode has multiple people. And the join table is Episode_Person.
My UI has a datatable with a filter on each column:
The filtering already works on Event and Address. And looks like this predicate in QueryDSL:
BooleanBuilder where = new BooleanBuilder();
if (pagination.getFilterBy().getMapOfFilters().get("eventno")!=null) {
where.and(qEpisode.eventno.containsIgnoreCase(pagination.getFilterBy().getMapOfFilters().get("eventno")));
}
if (pagination.getFilterBy().getMapOfFilters().get("address")!=null) {
where.and(qEpisode.address.formattedAddress.containsIgnoreCase(pagination.getFilterBy().getMapOfFilters().get("address")));
}
where.and(qEpisode.creatingUser.eq(user));
List<Episode> e = episodeRepository.findAll(where);
How would I now add a 3rd predicate for case name where case name is constructed of the first two people returned in the collection of people against a episode?
UPDATE
For clarification the DTO thats backs the UI view contains the "casename" attribute. It is created in the service layer when Domain objects are converted to DTO:
episodeDashboard.setNames(episodePersonList.get(0).getPerson().getSurname().toUpperCase() +" & " +episodePersonList.get(1).getPerson().getSurname().toUpperCase());
Not easily unless you delegate some of the processing to the database.
If we can get the case_name property to be populated at the database tier rather than as a derived property in the application logic then the front-end code becomes trivial.
We can do this by means of a view. The exact definition of this will depend on your database however the output would be something like this:
episode_summary_vw
+------------+-------------------------+
| epsiode_id | case_name |
+------------+-------------------------+
| 5 | Eastwood & Joilee|
| 6 | Pitt & Aniston|
| 7 | Aniston & Pitt|
+------------+-------------------------+
For Oracle it looks like LISTAGG function is what you would want and for MySQL the GROUP_CONCAT functions. In MySQL then I think this would look something like:
CREATE VIEW episode_summary_vw as
SELECT ep.episode_id, GROUP_CONCAT(p.surname SEPARATOR ' & ')
FROM episode_person ep
INNER JOIN person p on p.id = ep.person_id
GROUP BY ep.episode_id;
-- todo: needs limit to first 2 records
Once we have a view then we can simply map the case_name to the Episode entity using the #SecondaryTable functionality of JPA:
#Entity
#Table(name = "episodes")
#SecondaryTable(name = "episode_summary_vw", primaryKeyJoinColumna = #PrimaryKeyJoinColumn(name="episode_id", reference_column_name="id"))
public class Episode {
#Column(name ="case_name", table = "episode_summary_vw")
private String caseName;
}
You then filter and sort on the property as for any other field:
if (pagination.getFilterBy().getMapOfFilters().get("caseName")!=null) {
where.and(qEpisode.caseName.containsIgnoreCase(pagination.getFilterBy().
getMapOfFilters().get("caseName")));
}
I have a table name test;
mysql> desc test;
+--------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | 0 | |
| name | varchar(255) | YES | | NULL | |
| gendar | varchar(255) | YES | | NULL | |
+--------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> select * from test;
+----+------+--------+
| id | name | gendar |
+----+------+--------+
| 0 | John | male |
+----+------+--------+
1 row in set (0.00 sec)
And have a hibernate Entity like this:
#Entity
#Table(name = "test",schema = "", catalog = "mydb")
public class TestEntity {
private int id;
private String name;
private String gendar;
#Id
#Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "gendar")
public String getGendar() {
return gendar;
}
public void setGendar(String gendar) {
this.gendar = gendar;
}
}
The hibernate.xml is:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:mysql://localhost:3306/mydb</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.username">test</property>
<property name="connection.password">test</property>
<mapping class="me.armnotstrong.sql.TestEntity" />
<!-- DB schema will be updated if needed -->
<!-- <property name="hbm2ddl.auto">update</property> -->
</session-factory>
</hibernate-configuration>
The hibernate session is generated by this factory class:
public class HBSession {
private static final SessionFactory ourSessionFactory;
private static final ServiceRegistry serviceRegistry;
static {
try {
Configuration configuration = new Configuration();
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
ourSessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static Session getSession() throws HibernateException {
Session session = ourSessionFactory.openSession();
return session;
}
}
And I just add a user to the db by this
public class TestHb {
public static void main(String[] argvs){
Session session = HBSession.getSession();
TestEntity testEntity = new TestEntity();
testEntity.setName("John");
testEntity.setGendar("male");
Transaction tx = session.beginTransaction();
session.save(testEntity);
tx.commit();
session.close();
}
}
After run the TestHB code above to add a user to mysql, the session seemed to just hang there, and won't colse, diagnose using netstat -nap just proved my guess, but it's ok, I think. In fact, this just simulate the condition of a long connection to the db in the product environment which hibernate make.
The question comes that, when I alter the table when hibernate connection still there, The mysql client just blocked. and wont do the alter action as presumed unless I restart the mysql service. and in product environment, after restart the mysql service, one thing come after another, The hibernate app will not work anymore.
So what should I do to alter the table and keep my hibernate also working?
as required by #Florent, here is some addition info come out of command SHOW FULL PROCESSLIST and SHOW OPEN TABLES;
mysql> show open tables;
+----------+----------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+----------+--------+-------------+
| test | merchant | 0 | 0 |
| test | test | 0 | 0 |
| test | orders | 0 | 0 |
| test | product | 0 | 0 |
| test | codes | 0 | 0 |
+----------+----------+--------+-------------+
5 rows in set (0.02 sec)
mysql> show full processlist;
+-----+--------+-----------------+--------+---------+------+-------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+--------+-----------------+--------+---------+------+-------+-----------------------+
| 42 | test | localhost:35790 | test | Sleep | 4 | | NULL |
| 43 | test | localhost:35801 | test | Sleep | 4 | | NULL |
| 44 | test | localhost:35802 | test | Sleep | 4 | | NULL |
| 45 | test | localhost:35803 | test | Sleep | 4 | | NULL |
| 46 | test | localhost:35804 | test | Sleep | 4 | | NULL |
| 157 | test | localhost:51516 | test | Sleep | 174 | | NULL |
| 161 | test | localhost:53988 | test | Sleep | 174 | | NULL |
| 180 | test | localhost:58501 | test | Sleep | 174 | | NULL |
| 192 | test | localhost:47228 | test | Sleep | 7217 | | NULL |
| 193 | test | localhost:49372 | test | Sleep | 4485 | | NULL |
| 196 | test | localhost | test | Sleep | 9256 | | NULL |
| 197 | test | localhost | test | Sleep | 4555 | | NULL |
| 198 | test | localhost:42411 | test | Sleep | 4485 | | NULL |
| 200 | test | localhost | test | Query | 0 | NULL | show full processlist |
+-----+--------+-----------------+--------+---------+------+-------+-----------------------+
14 rows in set (0.00 sec)
I am getting the following error from my Hibernate code:
com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: Unknown column 'bulletin0_.bulletin_date' in 'field list'
There is no such bulletin_date column in my table, nor is there such a name in my model class. It's just called date. Here is the line where I'm getting the error.
Query query = session.createQuery("from Bulletin where approved = true");
Here is my model class (I'm leaving out the getters and setters):
public class Bulletin {
#Id
#Column(name="id")
#GeneratedValue
private int id;
#Column(name="date")
private String date;
#Column(name="name")
private String name;
#Column(name="subject")
private String subject;
#Column(name="note")
private String note;
#Column(name="approved")
private boolean approved;
}
Here is my table definition.
+----------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| date | varchar(10) | YES | | NULL | |
| name | varchar(30) | YES | | NULL | |
| subject | varchar(50) | YES | | NULL | |
| note | varchar(2500) | YES | | NULL | |
| approved | tinyint(1) | YES | | NULL | |
+----------+---------------+------+-----+---------+----------------+
I had the wrong column names in my Bulletin.hbm.xml file. When I corrected it, the problem was solved.
I have a table in my database, which contains sportsresults, and I need to select the last result for a competitor on a specific eventstage from a table.
I have this table:
+----------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+---------------+------+-----+---------+-------+
| EventStageID | int(11) | NO | PRI | NULL | |
| CompetitorID | int(11) | NO | PRI | NULL | |
| Lap | int(11) | NO | PRI | NULL | |
| Time | varchar(255) | YES | | NULL | |
| Status | varchar(255) | YES | | NULL | |
| PitstopCount | int(11) | YES | | NULL | |
| Grid | int(11) | YES | | NULL | |
| FastestLapTime | varchar(255) | YES | | NULL | |
| Substatus | varchar(255) | YES | | NULL | |
| Points | decimal(10,2) | YES | | NULL | |
| Position | int(11) | YES | | NULL | |
| StageType | int(11) | YES | | NULL | |
+----------------+---------------+------+-----+---------+-------+
I can select from the table with normal SQL query like this:
SELECT * FROM
(SELECT EventStageID as esi, CompetitorID as cid, Max(Lap) as MaxLap FROM srt_outright_season_event_stage_result_live WHERE EventStageID = 191666 GROUP BY CompetitorID) as y
LEFT JOIN
(SELECT * FROM srt_outright_season_event_stage_result_live) as x
ON x.CompetitorID = y.cid AND x.Lap = y.MaxLap AND x.EventStageID = y.esi;
Which gives the following result:
+--------+--------+--------+--------------+--------------+------+----------+--------+--------------+------+----------------+-----------+--------+----------+-----------+
| esi | cid | MaxLap | EventStageID | CompetitorID | Lap | Time | Status | PitstopCount | Grid | FastestLapTime | Substatus | Points | Position | StageType |
+--------+--------+--------+--------------+--------------+------+----------+--------+--------------+------+----------------+-----------+--------+----------+-----------+
| 191666 | 4521 | 0 | 191666 | 4521 | 0 | Finished | NULL | NULL | NULL | 2:00.175 | NULL | NULL | 4 | 5 |
| 191666 | 4524 | 0 | 191666 | 4524 | 0 | Finished | NULL | NULL | NULL | 2:04.053 | NULL | NULL | 10 | 5 |
| 191666 | 4533 | 0 | 191666 | 4533 | 0 | Finished | NULL | NULL | NULL | NULL | NULL | NULL | 13 | 5 |
| 191666 | 4538 | 0 | 191666 | 4538 | 0 | Finished | NULL | NULL | NULL | 2:01.218 | NULL | NULL | 6 | 5 |
| 191666 | 5769 | 0 | 191666 | 5769 | 0 | Finished | NULL | NULL | NULL | 2:00.050 | NULL | NULL | 3 | 5 |
| 191666 | 7135 | 0 | 191666 | 7135 | 0 | Finished | NULL | NULL | NULL | 1:59.431 | NULL | NULL | 1 | 5 |
| 191666 | 7138 | 0 | 191666 | 7138 | 0 | Finished | NULL | NULL | NULL | NULL | NULL | NULL | 18 | 5 |
| 191666 | 7610 | 0 | 191666 | 7610 | 0 | Finished | NULL | NULL | NULL | 1:59.486 | NULL | NULL | 2 | 5 |
+--------+--------+--------+--------------+--------------+------+----------+--------+--------------+------+----------------+-----------+--------+----------+-----------+
I have this Entity class:
#Entity(name = "event_stage_result_live")
public class EventStageResultLive {
#EmbeddedId
private PKEventStageResultLive pkEventStageResultLive;
// Composite PK contains EventStageID, CompetitorID and Lap
#Column(name = "Time")
private String time;
#Column(name = "Status")
private String status;
#Column(name = "PitstopCount")
private Integer pitstopCount;
#Column(name = "Grid")
private Integer grid;
#Column(name = "Position")
private Integer position;
#Column(name = "FastestLapTime")
private String fastestLapTime;
#Column(name = "Substatus")
private String substatus;
#Column(name = "Points")
private Float points;
#Column(name = "StageType")
private StageType stageType;
// getters and setters...
}
I think in SQL you can do something like this. I dont think join is required.
select * from srt_outright_season_event_stage_result_live c
where c.CompetitorID = :competitorID and c.EventStageID = 191666
and c.Lap = (select max(d.lap) from srt_outright_season_event_stage_result_live d
where d.CompetitorID = :competitorID and d.EventStageID = 191666 )
Passed to JPQL is
select e from EventStageResultLive e
where e.pkEventStageResultLive.CompetitorID = :competitorID and c.pkEventStageResultLive.EventStageID = 191666
and e.pkEventStageResultLive.Lap = (select max(d.pkEventStageResultLive.lap) from EventStageResultLive d
where d.pkEventStageResultLive.CompetitorID = :competitorID and d.pkEventStageResultLive.EventStageID = 191666 )
Assuming
public class PKEventStageResultLive{
private int CompetitorID ;
private int EventStageID ;
private int Lap;
}
If the name of the properties are different correct the name in the JPQL
And competitorID as a named parameter.