I'm having an issue performing a custom query through the use of a spring data jpa repository.
I have a repository class implementing JPARepository<>. Everything works as expected for all of the built-in CRUD queries along with some custom queries, but doing qualification among inner collections isn't working and is returning back a full result set as though the qualification of the collection did not exist.
For example, here is a query:
public interface MessageRepository extends JpaRepository<Message, Integer> {
#Query("SELECT a FROM Message a, Message_Topic b WHERE a.systemNm = :theSystem AND a. applicationNm = :theApplication AND b.topicNm = :theTopicName AND a.insertTs BETWEEN :theStartDate AND :theEndDate AND a.expirationDt > CURRENT_TIMESTAMP")
List<Message> findMessagesByTopic(#Param("theSystem") String theSystem,
#Param("theApplication") String theApplication,
#Param("theTopicName") String theTopicName,
#Param("theStartDate") Date theStartDate,
#Param("theEndDate") Date theEndDate);
With the following JPA entities:
Message:
#Entity
public class Message implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="message_id")
private int messageId;
#Column(name="application_nm")
private String applicationNm;
#Column(name="execution_instance_txt")
private String executionInstanceTxt;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="expiration_dt")
private Date expirationDt;
#Column(name="grouping_des")
private String groupingDes;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="insert_ts")
private Date insertTs;
#Column(name="message_detail_txt")
private String messageDetailTxt;
#Column(name="message_summary_txt")
private String messageSummaryTxt;
#Column(name="severity_des")
private String severityDes;
#Column(name="system_nm")
private String systemNm;
//uni-directional many-to-one association to Message_Topic
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name="message_id", referencedColumnName="message_id")
private Set<Message_Topic> messageTopics;
Message_Topic:
#Entity
public class Message_Topic implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="message_topic_id")
private int messageTopicId;
#Column(name="message_id", insertable=false, updatable=false)
private int messageId;
#Column(name="topic_nm")
private String topicNm;
#Column(name="topic_value_txt")
private String topicValueTxt;
This is your query:
SELECT a FROM Message a, Message_Topic b WHERE a.systemNm = :theSystem AND a. applicationNm = :theApplication AND b.topicNm = :theTopicName AND a.insertTs BETWEEN :theStartDate AND :theEndDate AND a.expirationDt > CURRENT_TIMESTAMP
Where are Message and Message_Topic joined?, If you transform this query to a native query, is possible you can detect the fault.
Related
I'm having a problem with a mapstruct mapper. When running mvn clean install (or mvn clean compile), I get these errors:
[ERROR] /mapper/EntityMapper.java:[28,7] Can't map property "java.util.List<com.socomec.tseselector.model.Source> architecture.sources
" to "java.lang.Integer architecture.sources". Consider to declare/implement a mapping method: "java.lang.Integer map(java.util.List<com.socomec.tseselector.model.Source> value)".
[ERROR] /mapper/command/TSEProjectCommandMapper.java:[21,16] Can't map property "java.lang.Integer architecture.loads" to "java.util.List<com.socomec.tseselector.model.Load> architecture.loads". Consider to declare/implement a mapping method: "java.util.List<com.socomec.tseselector.model.Load> map(java.lang.Integer value)".
The problem is that I have no idea where mapstruct is getting this "java.lang.Integer architecture.loads" from.
I don't understand where this Integer is coming from, as you can see in my code there is no Integer. Also up until now I never ran into this error while using similar mapper.
Here is my entity Architecture :
public class Architecture {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String imagePath;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="tse_architectures_loads",
joinColumns = {#JoinColumn(table = "tse_architecture", name = "architecture_id")},
inverseJoinColumns = {#JoinColumn(table = "tse_load", name = "load_id")}
)
private List<Load> loads;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="tse_architectures_sources",
joinColumns = {#JoinColumn(table = "tse_architecture", name = "architecture_id")},
inverseJoinColumns = {#JoinColumn(table = "tse_source", name = "source_id")}
)
private List<Source> sources;
private String techno;
public Architecture() {
this.loads = new ArrayList<>();
this.sources = new ArrayList<>();
}
The DTO
public class ArchitectureDto {
private Long id;
private String name;
private String imagePath;
private List<LoadDto> loadDtos;
private List<SourceDto> sourceDtos;
private String techno;
public ArchitectureDto() {
this.loadDtos = new ArrayList<>();
this.sourceDtos = new ArrayList<>();
}
}
My mapper:
#Mapper(componentModel = "spring")
public interface ArchitectureMapper extends EntityMapper<ArchitectureDto, Architecture> {
#Mapping(source = "loads", target = "loadDtos")
#Mapping(source = "sources", target = "sourceDtos")
ArchitectureDto toDto(Architecture architecture);
}
Entity Mapper:
public interface EntityMapper<D, E> {
/**
* Map a DTO to an Entity
*
* #param dto the dto to map
* #return an Entity
*/
E toEntity(D dto);
/**
* Map an Entity to a DTO
*
* #param entity to map to a DTO
* #return a DTO
*/
D toDto(E entity);
/**
* Map a List of DTOs to a List of Entities
*
* #param dtoList the list to map
* #return a list of Entities
*/
List<E> toEntity(List<D> dtoList);
/**
* Map a list of Entities to a list of DTOs
*
* #param entityList the list to map
* #return a list of DTOs
*/
List<D> toDto(List<E> entityList);
}
Looking through the documentation and other questions about mapstruct here, I have not been able to find a solution so far.
I'm addind my load and source classes
Load:
public class Load {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String type;
private String unity;
private Long sourcePriority1;
private Long SourcePriority2;
private Long SourcePriority3;
private Long kvaValue;
private Long kwValue;
private Long pfValue;
private Long aValue;
private Long iccValue;
#JsonIgnore
#ManyToMany(mappedBy = "loads")
private List<TSEProject> tseProjects;
#JsonIgnore
#ManyToMany(mappedBy = "loadList")
private List<Architecture> architectures;
public Load() {
this.architectures = new ArrayList<>();
this.tseProjects = new ArrayList<>();
}
Source:
public class Source {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String type;
private String unity;
private Long quantity;
private Long kvaValue;
private Long kwValue;
private Long pfValue;
private Long aValue;
#JsonIgnore
#ManyToMany(mappedBy = "sources")
private List<TSEProject> tseProjects;
#JsonIgnore
#ManyToMany(mappedBy = "sourceList")
private List<Architecture> architectures;
public Source() {
this.architectures = new ArrayList<>();
this.tseProjects = new ArrayList<>();
}
}
and the DTOs
#Data
public class LoadDto {
private Long id;
private String name;
private String type;
private String unity;
private Long sourcePriority1;
private Long SourcePriority2;
private Long SourcePriority3;
private Long kvaValue;
private Long kwValue;
private Long pfValue;
private Long aValue;
private Long iccValue;
private List<Long> tseProjectsId;
public LoadDto() {
this.tseProjectsId = new ArrayList<>();
}
}
#Data
public class SourceDto {
private Long id;
private String name;
private String type;
private String unity;
private Long quantity;
private Long kvaValue;
private Long kwValue;
private Long pfValue;
private Long aValue;
}
I guess you need to troubleshoot a bit more.. Try to aid MapStruct and define an additional mapping method like this:
Mapper(componentModel = "spring")
public interface ArchitectureMapper extends EntityMapper<ArchitectureDto, Architecture> {
#Mapping(source = "loads", target = "loadDtos")
#Mapping(source = "sources", target = "sourceDtos")
ArchitectureDto toDto(Architecture architecture);
// first try with all properties ignored
// #Mapping( target = "id", ignore = true ) etc.
SourceDto toDto(Source source);
// first try with all properties ignored
// #Mapping( target = "id", ignore = true ) etc.
LoadDto toDto(Load load);
}
The problem is that MapStruct tries to map these objects automatically based on property names. If it somewhere discovers the same name it tries to do type conversion or to use existing mapping methods to do its job. It even tries 2 steps (so conversion and then a method), which sometimes leads to these results.
You could provide some of the mappings MapStruct tries to generate itself and then again ignore all properties like indicated in the example above. One by one remove the ignore lines and see when MapStruct starts to complain.
If its again an object graph, you could apply a similar strategy.
It's a bit tedious debugging like this, but probably deep down in your object graphs you discover the problem. You could then remove all signatures again, except the one that solved your problem, making maximum use of code generation.
I have these Objects:
#Data
#Entity
#Table
#EqualsAndHashCode(callSuper = true)
public class User extends AbstractEntity implements Serializable {
private static final long serialVersionUID = -55089179131569489L;
private String username;
private String email;
private boolean admin;
private String name;
private String surname;
#OneToMany(mappedBy = "owner")
private List<Ad> ads;
}
and
#Entity
#Table
#Data
#EqualsAndHashCode(callSuper = true)
public class Ad extends AbstractEntity implements Serializable {
private static final long serialVersionUID = -4590938091334150254L;
private String name;
private String description;
private double price;
#Enumerated(EnumType.STRING)
private Category category;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "OWNER_ID")
private User owner;
}
When I try to execute a POST with an object of type Ad.class with inside an existing object of type User.class (already in the Database) the service saves only the Ad object and the join column "OWNER_ID" remains empty.
I think that the mapping is correct. Could you help me to figure out the problem?
This is my Repository:
#Repository
#Transactional(readOnly = true)
public interface AdRepository extends PagingAndSortingRepository<Ad, String>
{}
and this is my RestRepository
#RepositoryRestResource(collectionResourceRel = "ad", path = "ad")
public interface AdRestRepository extends PagingAndSortingRepository<Ad, String> {}
If I step back a little and generalize your problem,
You are trying to POST a sub resource and expect both actions of
making a new resource (Ad)
making association with the owner (User)
to be happened with a single call.
But unfortunately spring-data-rest does not support such a behavior. You need 2 calls to do this.
One to make the resource (Ad) => POST to /ads with actual payload
Second to make the association => POST to users/{ownerId} with the hateoas link of the resource created by the first call.
Take a look at this section of official documentation.
I am trying to make a database with these models
Report model
#DatabaseTable(tableName = "report")
public class Report {
#DatabaseField(generatedId = true)
private int id;
#DatabaseField(columnName = "client_name")
private String client;
#ForeignCollectionField(maxEagerLevel = 2, eager = true)
private Collection<Product> productList;
Product model
#DatabaseTable(tableName = "product")
public abstract class Product implements Comparable<Product> {
#DatabaseField(generatedId = true)
private int id;
#DatabaseField(foreign = true)
private Report report;
#DatabaseField(columnName = "instance")
private String instance;
#DatabaseField(columnName = "product_type")
private String productType;
So I am able to successfully insert in the tables,
but when trying out:
List<Report> reports = reportDao.queryBuilder().where().eq("client_name", clientName).query();
I get an exception
Could not create object for class model.product.Product
So it makes sense, because Product is abstract, but is there a way using the "Instance" attribute from Product interface which indicates what kind of implementation of product it is, to create the appropriate object while running the query?
Thank you
I'm using spring data and I made this query:
#Query("SELECT DISTINCT u.pk.fleet.fleetName FROM FleetHasUser u WHERE u.pk.user.username = ?1")
List<FleetName> allFleetNameForUser(String username);
The aim of this query is find all FleetName for a specific user.
This is the part of database schema interested in:
The FleetHasUser class:
#Entity
#Table(name = "fleet_has_user", catalog = "dart")
#AssociationOverrides({
#AssociationOverride(name = "pk.user",
joinColumns = #JoinColumn(name = "id_username")),
#AssociationOverride(name = "pk.fleet",
joinColumns = #JoinColumn(name = "id_fleet")) })
public class FleetHasUser implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private FleetHasUserKeys pk = new FleetHasUserKeys();
#EmbeddedId
public FleetHasUserKeys getPk() {
return pk;
}
the FleetHasUserKeys class:
#Embeddable
public class FleetHasUserKeys implements Serializable {
private static final long serialVersionUID = 1L;
protected User user;
protected Fleet fleet;
Fleet class:
#Entity
#Table(name = "fleet", catalog = "dart")
public class Fleet implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer idFleet;
private Ecu ecu;
private String application;
private Cubic cubic;
private Integer power;
private String euroClass;
private String engineType;
private Traction traction;
private String transmission;
private String note;
private FleetName fleetName;
#JsonIgnore
private Set<Car> cars = new HashSet<Car>(0);
#JsonIgnore
private Set<FleetHasUser> fleetHasUsers = new HashSet<FleetHasUser>(0);
As you can see FleetName is a class. I tried without distinct and the list had duplicated elements, so before create a subquery I inserted distinct only for test and it worked. I didn't find information on google, but is it possible to use distinct on a object? How does it work(check the primary key fields)?
Maybe may I even create a Spring data query like findDistinctPkFleetFleetNameByPkUserUsername?
Thanks
As far as i know, the distinct is being applied on the query not on the mapping phase, as you can see #Query doc the annotation only executes jpql queries so a native distinct.
As you say other option to execute a distinct is
Spring data Distinct
You cannot apply "Distinct" to objects, but you can map then on Set's to avoid duplicated, but the better approach is filter properly the data on the database.
I'm not able to get the total count of my query.
JPAQuery query = super.prepareJPAQuery(userAccountHasWorkgroup).where(
userAccountHasWorkgroup.workgroup.id.eq(workgroupId);
query.count();
userAccountHasWorkgrouphas an #Embeddable class as ID.
javax.ejb.EJBTransactionRolledbackException: org.hibernate.exception.DataException: Operand should contain 1 column(s)
I add more information:
#Entity
#Table(name = "UserAccount_has_Workgroup")
public class UserAccountHasWorkgroup implements java.io.Serializable {
private static final long serialVersionUID = 6537213525312531347L;
private UserAccountHasWorkgroupId id;
private UserAccount userAccountByUserAccountId;
private Privilege privilege;
private UserAccount userAccountByApprovedByUserAccountId;
private Workgroup workgroup;
private boolean approved;
private boolean lastActiveWorkgroup;
private boolean isWorkgroupReferent;
...
}
#Embeddable
public class UserAccountHasWorkgroupId implements java.io.Serializable {
private static final long serialVersionUID = 6368469866573301127L;
private long userAccountId;
private long workgroupId;
...
}
If i do:
List<UserAccountHasWorkgroup> result = query.list(userAccountHasWorkgroup);
it works, but when I try to count:
Long count = query.count();
I receive the DataException
Instead of:
query.count();
I do
query.uniqueResult(Wildcard.count.as("count"));
It seems to work properly.