Mapping a concrete selection to object with HibernateTemplate - java

Im using HibernateTemplate3 + Spring, and I find myself needing a list of ProfilingReport objects by date, grouping by their average times.
The problem is that apparently Hibernate can not map my selection to the ProfilingReport object from my model.
Id like to know if there is a way to do this, since it is just returning a list of arrays of objects at the moment.
This is the beggining of my ProfilingReport class (minus getters and setters):
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Temporal;
#Entity
#Table(name = "ProfilingReport")
public class ProfilingReport extends Persistent {
private String serviceName;
private long runTime;
#Temporal(javax.persistence.TemporalType.DATE)
private Date date;
My Persistent class from which all persistent classes extend:
import java.io.Serializable;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
#MappedSuperclass
public class Persistent implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Long version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
And this is the method im trying to execute in my DAO:
#Override
#Transactional(readOnly = true)
public List<ProfilingReport> getProfilingReports(final Date fecha) {
String query = "select p.id, p.version, p.serviceName, AVG(p.runTime), date "
+ "from Profiling p "
+ "where p.date = :fecha "
+ "group by p.serviceName";
return this.hibernateTemplate.findByNamedParam(query, "fecha", fecha);
}

Try this hql changes,
#Override
#Transactional(readOnly = true)
public List<ProfilingReport> getProfilingReports(final Date fecha) {
String query = "from ProfilingReport p "
+ "where p.date = :fecha "
+ "group by p.serviceName";
return this.hibernateTemplate.findByNamedParam(query, "fecha", fecha);
}
Then you need to find average of runTime by java business logic itself.
Otherwise with your existing list of array object you can iterate and create ProfilingReport object and assign these array values to relevant properties in ProfilingReport. Finally add each ProfilingReport object into a list(List).

On solution would be to use Hibernate org.hibernate.annotations.Formula annotation:
#Entity
#Table(name = "ProfilingReport")
public class ProfilingReport extends Persistent {
private String serviceName;
private long runTime;
#Temporal(javax.persistence.TemporalType.DATE)
private Date date;
#Formula(
"select AVG(p.runTime) from Profiling p group by p.serviceName"
)
private Number averageRunTime;
}
And then just run your query by date:
#Override
#Transactional(readOnly = true)
public List<ProfilingReport> getProfilingReports(final Date fecha) {
return this.hibernateTemplate.findByNamedParam(
"from ProfilingReport p where p.date = :fecha",
"fecha",
fecha
);
}
Another solution is to use SQL window functions if your database supports them, then you just need a native query and the window function allows you to retain the selected rows while embedding the aggregate result, just like you wanted in the first place.

Related

Query returning a lot of repeated records

I try to make query from two tables (Statement, AppCurContract), but receive a lot of repeated records. Even if I make query only from Statement, I receive the same result.
It started when I added appCurContracts field to the Statement bean.
I found same question here Spring Data JPA query return repeated row instead of actual data, why?
But I have unique key in both tables. What am I doing wrong?
Here is my code
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
#Entity
#Table
#Data
#EqualsAndHashCode(exclude = "appCurContracts")
public class Statement {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String numStatement;
#Column(updatable = false)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateTimeSubmStatement;
private int filialId;
private int myself;
private int status;
private Date modifyDate;
private String nameNonResident;
private String email;
private Integer typeStatement;
#OneToMany(mappedBy = "statement", cascade = CascadeType.ALL)
private Set<AppCurContract> appCurContracts;
public Statement() {
super();
}
public Statement(String nameDocument, String numStatement, LocalDateTime dateTimeSubmStatement, String jurPerson, String iin_bin, int filialId, int myself, int status, Date modifyDate, String nameNonResident, String contractNum, Date contractDate, String phone, String email, Integer typeStatement, String json, String iinBinRight, AppCurContract... appCurContracts) {
this.numStatement = numStatement;
this.dateTimeSubmStatement = dateTimeSubmStatement;
this.filialId = filialId;
this.myself = myself;
this.status = status;
this.modifyDate = modifyDate;
this.nameNonResident = nameNonResident;
this.email = email;
this.typeStatement = typeStatement;
this.appCurContracts = Stream.of(appCurContracts).collect(Collectors.toSet());
this.appCurContracts.forEach(x -> x.setStatement(this));
}
public void setAppCurContracts(Set<AppCurContract> appCurContracts) {
for (AppCurContract child : appCurContracts) {
child.setStatement(this);
}
this.appCurContracts = appCurContracts;
}
}
import lombok.Data;
import javax.persistence.*;
#Entity
#Data
public class AppCurContract {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn()
private Statement statement;
private String jurPerson;
private String iin_bin;
private String nameDocument;
private String contractNum;
private String contractDate;
public AppCurContract() {
super();
}
public AppCurContract(String jurPerson, String iin_bin, String nameDocument, String contractNum, String contractDate) {
this.jurPerson = jurPerson;
this.iin_bin = iin_bin;
this.nameDocument = nameDocument;
this.contractNum = contractNum;
this.contractDate = contractDate;
}
}
public interface StatementRepo extends JpaRepository<Statement, Long> {
#Query("SELECT d FROM Statement d JOIN d.appCurContracts e" +
" WHERE d.status = ?1")
// #Query("SELECT d FROM Statement d WHERE d.status = ?1")
List<Statement> findByStatus(Integer status);
List<Statement> findStatementsByEmailEquals(String email);
}
EDIT
Looking carefully at the JSON result I discovered that the result is not just repeated, but the field "appCurontract" contains nested statements then again "appCurContract" (nested in each other), etc. I think so indefinitely.
I expect only 5 records.
Define the hashcode method yourself, with whatever condition necessary as set uses this to check for duplicates. Your code (in your repository implementation) will then become:
List<Statement> findDistinctByEmail(String email);
And you should not need the Query annotation.
Remove the setAppCurContracts method on Statement class. Is not necessary.
Remove the #Query's annotation and use the findByStatus method.
I found a solution to the problem. I added #JsonIgnore annotation to the Statement field in AppCurContract class.
public class AppCurContract {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonIgnore
#ManyToOne
#JoinColumn()
private Statement statement;
private String jurPerson;
private String iin_bin;
private String nameDocument;
private String contractNum;
private String contractDate;
Thank you all for participating

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: no session or session was closed

I am specifying my entity as follows
package com.drishti.training.dbentity;
import java.util.List;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import com.drishti.dacx.core.framework.ameyoentitytypes.AbstractDBEntity;
/**
*
*/
#Entity
#Table(name = "template")
public class TemplateDBEntity extends AbstractDBEntity {
String template_name, organisationId;
#Column(name = "organisation_id", nullable = false)
public String getOrganisationId() {
return organisationId;
}
public void setOrganisationId(String organisationId) {
this.organisationId = organisationId;
}
private String templateId;
// private List<Integer> listOfTrainingIds;
private List<String> listOfTrainingIds;
#Id
#Column(name = "template_id", nullable = false)
public String getTemplateId() {
return templateId;
}
public void setTemplateId(String templateId) {
this.templateId = templateId;
}
#ElementCollection(targetClass = String.class)
#CollectionTable(name = "template_id_vs_training_id", joinColumns = #JoinColumn(name = "template_id"))
#Column(name = "training_id", nullable = false)
public List<String> getListOfTrainingIds() {
return listOfTrainingIds;
}
public void setListOfTrainingIds(List<String> listOfTrainingIds) {
this.listOfTrainingIds = listOfTrainingIds;
}
#Column(name = "template_name")
public String getName() {
return template_name;
}
public void setName(String name) {
this.template_name = name;
}
}
and
another table is
create table template_id_vs_training_id
(
template_id varchar references template(template_id) on delete cascade,
training_id varchar references training(training_id) on delete cascade,
PRIMARY KEY (template_id,training_id)
);
but when i load the TemplateDBEntity it provides me the above reported error.
LazyInitializationException, as hibernate documentation says:
Indicates an attempt to access not-yet-fetched data outside of a
session context. For example, when an uninitialized proxy or
collection is accessed after the session was closed
The only cause of this exception is listOfTrainingIds property as it's an
#ElementCollection which is Lazy loaded by default, so either :
Make sure that you're accessing listOfTrainingIds property inside a transaction (Your entity can use available session to fetch your collection).
Or make it eager #ElementCollection(fetch = FetchType.EAGER), but be aware that every time your select the entity from the database, your collection will be loaded eagerly as well, even if you don't need it, which can impact performance.
Or use fetch keyword in your hibernate Query (if you're using query to load your entity):
List<TemplateDBEntity> TemplateDBEntitys = session.createQuery(
"select t from TemplateDBEntity t join fetch t.listOfTrainingIds",
TemplateDBEntity.class).getResultList();
Or use #FetchProfile.
// In class...
#FetchProfile(
name = "withListOfTrainingIds",
fetchOverrides = {#FetchProfile.FetchOverride(mode = FetchMode.JOIN, association = "listOfTrainingIds", entity = TemplateDBEntity.class)})
public class TemplateDBEntity extends AbstractDBEntity {
//...
}
// To get your entity
session.enableFetchProfile("withListOfTrainingIds");
System.out.println(session.get(TemplateDBEntity.class, templateId));
session.disableFetchProfile("withListOfTrainingIds");
I prefer the last two options, as hibernate will perform one query to database, even you keep the collection lazy loaded, which is better for performance.

java.sql.SQLException: ORA-00942: table or view does not exist with JPA entityManager.createQuery()

I am using JPA createquery API to fetch the data.
Here is my query data
#PersistenceContext
EntityManager entityManager;
#Override
public List<String> fetchAllReleaseNumbers() {
Query query = entityManager.createQuery("SELECT release FROM ReleaseModel", String.class);
return query.getResultList();
}
and here is my pojo class.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "dbname.tablenamefromDB")
public class ReleaseModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "dbcolumnname", unique = true, nullable = false)
private String release;
#Column(name = "dbcolumnname")
private String releaseDesc;
#Column(name = "dbcolumnname")
private Integer releaseStatus;
#Column(name = "dbcolumnname")
private Integer releaseMode;
public String getRelease() {
return release;
}
public void setRelease(String release) {
this.release = release;
}
public String getReleaseDesc() {
return releaseDesc;
}
public void setReleaseDesc(String releaseDesc) {
this.releaseDesc = releaseDesc;
}
public Integer getReleaseStatus() {
return releaseStatus;
}
public void setReleaseStatus(Integer releaseStatus) {
this.releaseStatus = releaseStatus;
}
public Integer getReleaseMode() {
return releaseMode;
}
public void setReleaseMode(Integer releaseMode) {
this.releaseMode = releaseMode;
}
}
Though the table exists in db its throwing not exist.Any ideas where I made mistake.
I tried whether any aliases can be given to the table name.
I am using pojo class name only for createQuery.
TIA.
You should specify a schema name by this way
#Table(schema = "dbname", name = "tablenamefromDB")
You have an incorrect mapping:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "dbcolumnname", unique = true, nullable = false)
private String release;
I think String can't be auto generated.
Also all your columns have dbcolumnname name.
The issue was that the schema was not specified in the entity class or the user did not login using proxy. If the user login using a proxy access i.e. userName[schemaName] they do not need to specify schema in the entity class. But if the user login using just the userName, they need to specify the schema in the entity. This is to specify where the table can be found in the database.

Unable to display data from relationships in templates

I'm stuck with trying to display data for a One-to-One relationship in Twirl templates (using Play Framework Java - 2.5.10). Basically I have a User model:
package models;
import java.sql.Date;
import javax.persistence.*;
import com.avaje.ebean.Model;
#Entity
#Table(name = "users")
public class User extends Model {
#Id
#Column(name = "id")
public Long id;
#Column(name = "first_name")
public String firstName;
#Column(name = "middle_name")
public String middleName;
#Column(name = "last_name")
public String lastName;
#Column(name = "date_of_birth")
public Date dateOfBirth;
#Column(name = "sex")
public String sex;
#OneToOne
#JoinColumn(name = "time_zone_id")
public TimeZone timeZone;
public static Finder<Long, User> find = new Finder<>(User.class);
}
and the Farmer model:
package models;
import com.avaje.ebean.Model;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name="farmers")
public class Farmer extends Model {
public enum Status {INACTIVE, ACTIVE}
#Id
#Column(name="id")
public Long id;
#OneToOne(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="user_id")
public User user;
#Column(name="profile_pic_url")
public String profilePicUrl;
#Column(name="access_url")
public String accessUrl;
#Column(name="status")
public String status = Status.INACTIVE.name();
#OneToMany(mappedBy = "farmer", targetEntity = Farm.class, fetch = FetchType.LAZY)
public List<Farm> farms;
public static Finder<Long, Farmer> find = new Finder<>(Farmer.class);
public static List<Farmer> getAllActive() {
return Farmer.find.where().eq("status", Status.ACTIVE.name()).findList();
}
}
Notice there's a one-to-one with User model with fetch type set to eager. Now, I want to display data of farmers in my template, where a farmer's name is actually the name in the associated User model.
So I did this in my controller:
public class FarmerController extends Controller {
public Result all() {
return ok(farmers.render(Farmer.getAllActive()));
}
public Result farmer(Long id, String url) {
return ok(farmer.render());
}
}
Now this gets me the right farmer data, but when I try to display the name via the User model, I get null. More specifically, writing this results in nulls (I get nullnull, actually):
<div><h4>#(farmer.user.firstName + farmer.user.lastName)</h4></div>
What am I missing?
As discussed at the comments, this is because play-enhancer does not works for views or any Scala code at all. Since Twirl compiles scala.html code to scala code, this compiled code is not touched by the enhancer.
The solution is then to manually create the get for the relationship:
public class Farmer extends Model {
public User getUser() {
return this.user;
}
}
This is Java code and then will be handled as expected. Of course, you have to change your views to use farmer.getUser instead of farm.user.
Also, as stated at the docs, byte code enhancement involves some magic. But you can avoid it at all and just use regular POJOs (with explicitly declared gets and sets).

Spring JPA + CRUD - custom query doesn't allow _ characters?

I'm having troubles in creating a custom query within spring, because my Entity contains an "_" character in it's parameter's name: "game_date".
My table has a column named "game_date" as well.
I have created following method:
List<Games> findByGame_dateAndOpponent(#Param("game_date") Date game_date, #Param("opponent") String opponent);
but when I start my app, it's crashing with exception of kind: "org.springframework.data.mapping.PropertyReferenceException: No property gamedate found for type Games!". After changing a parameter name to the "gameDate" both in Entity and Query method, it stopped complaining, and is actually returning expected entries. But at the same time, it doesn't return values from the column "game_date", in the search queries, which is a simple regular column of a Date type. I have no idea what's going on with all this thing.
DB I'm using is MySql.
Here comes the code itself:
Entity:
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
#Entity
#Table(name = "games")
public class Games {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id_game")
private int id;
#Column(name = "game_date", columnDefinition = "DATE")
#Temporal(TemporalType.DATE)
private Date gameDate;
public Date getGame_date() {
return gameDate;
}
public void setGame_date(Date _game_date) {
this.gameDate = _game_date;
}
}
And a repository:
import java.sql.Date;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
#RepositoryRestResource
public interface GamesRepository extends CrudRepository< Games , Integer > {
List< Games > findById( #Param( "id" ) int id );
List< Games > findAll( );
List<Games> findByGameDateAndOpponent(#Param("game_date") Date game_date, #Param("opponent") String opponent);
}
The underscore is a reserved keyword in Spring Data JPA. It should be enough to remove it from your property and from its getters and setters and Hibernate will do the rest:
#Entity
#Table(name = "games")
public class Games {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id_game")
private int id;
//Getter and setters for id
#Column(name = "game_date")
private Date gameDate;
public Date getGameDate() {
return gameDate;
}
public void setGameDate(Date gameDate) {
this.gameDate = gameDate;
}
}
Also, in general, try to use java naming convention for variable and field names, which is mixed case with lowercase first.
See also:
Spring Data JPA repository methods don't recognize property names with underscores

Categories

Resources