How do I use JPA's #EmbeddedId with #Query and no #Table? - jpa

I have a the following...
#Query(
value="SELECT MAX(LOG_DATE) AS LOG_DATE, REGION_NAME, HOST_NAME, MIN(REGION_MIN_TIME) AS REGION_MIN_TIME, MAX(REGION_MAX_TIME) AS REGION_MAX_TIME,SUM(TOTAL_TIME_TAKEN) AS TOTAL_TIME_TAKEN, SUM(REGION_API_COUNT) AS REGION_API_COUNT,AVG(TOTAL_TIME_TAKEN/REGION_API_COUNT) AS AVG_RES_TIME, MAX(LST_SRC_UPDT_DATE) AS LST_SRC_UPDT_DATE FROM MY_SCHEMA.GEMFIRE_REGION_USAGE GROUP BY REGION_NAME,HOST_NAME",
nativeQuery = true
)
List<GemfireStatAggregate> findAggregates();
#Getter
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class GemfireStatAggregate {
#EmbeddedId
private GemfireStatId id;
#Column(name="REGION_MIN_TIME")
private String regionMinTime;
}
#Embeddable
#Getter
#AllArgsConstructor
#NoArgsConstructor
public class GemfireStatId implements Serializable {
#Column(name = "LOG_DATE")
private Date loggedDate;
#Column(name="REGION_NAME")
private String regionName;
#Column(name="HOST_NAME")
private String hostName;
}
But when I run I get the following...
Failed to convert from type [java.lang.Object[]] to type [com.me.GemfireStatAggregate] for value '{...data redacted but looks good...}'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.sql.Timestamp] to type [com.me.GemfireStatAggregate]
Why is this happening?
Update
This does work but is ugly and I don't like it...
public List<GemfireStatAggregate> getAggregateData() {
List<GemfireStatAggregate> result = new ArrayList<>();
for(Object[] arr : repo.findAggregates()){
GemfireStatId id = new GemfireStatId(
(Timestamp) Array.get(arr, 0),
(String) Array.get(arr, 1),
(String) Array.get(arr, 2)
);
result.add(new GemfireStatAggregate(
id,
(String) Array.get(arr, 3)
));
}
return result;
}

add #Temporal annotation on your loggedDate field
#Temporal(TemporalType.DATE)
#Column(name = "LOG_DATE")
private Date loggedDate;

Related

EntityListener saving null value in subclass

I have a Customer entity class and a CorporateCustomer entity class, CorporateCustomer extends Customer and Customer extends AuditEntity and AuditEntity extends BaseEntity. Both AuditEntity and BaseEntity have EntityListener and implemented prePersist value.
CorporateCustomer Entity
#Data
#Entity
#Table(name = "t_cust_corp")
#Where(clause = "sts_cd not in ('D', 'I')")
#TypeDef(typeClass = JsonBinaryType.class, name = "jsonb")
public class CorporateCustomer extends Customer {
#Column(name = "cust_id")
private UUID custId;
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb", name = "obligor_dtl")
private Serializable obligorDetail;
}
Customer Entity
#Data
#Entity
#Table(name = "t_customer")
#Where(clause = "sts_cd not in ('D','I')")
#Inheritance(strategy = InheritanceType.JOINED)
public class Customer extends BaseEntity {
#Column(name = "cif_no")
private String cifNo;
#Column(name = "cust_name")
private String customerName;
#Column(name = "mt_cust_typ_cd")
private String customerTypeCd;
#Column(name = "mt_cust_id_typ_cd")
private String customerIdTypeCd;
#Column(name = "cust_id_no")
private String customerIdNo;
}
BaseEntity (Library Generated)
#Generated
protected BaseEntity(final BaseEntity.BaseEntityBuilder<?, ?> b) {
this.id = b.id;
this.statusCode = b.statusCode;
this.version = b.version;
this.createdBy = b.createdBy;
this.createdTime = b.createdTime;
this.lastUpdatedBy = b.lastUpdatedBy;
this.lastUpdatedTime = b.lastUpdatedTime;
}
BaseEntityListener (part of codes)
#PrePersist
public void prePersist(BaseEntity baseEntity) {
log.trace("prePersist baseEntity:" + baseEntity);
baseEntity.setCreatedBy(requestSynchronizationManager.getUserIdForAudit());
baseEntity.setCreatedTime(OffsetDateTime.now());
baseEntity.setVersion(0);
if (baseEntity.getStatusCode() == null) {
baseEntity.setStatusCode(StatusCode.INACTIVE);
}
}
AuditEntity
#Data
#SuperBuilder
#MappedSuperclass
#NoArgsConstructor
#AllArgsConstructor
#EntityListeners({AuditEntityListener.class})
public abstract class AuditEntity extends BaseEntity {
private static final long serialVersionUID = 1778847390000617814L;
#Column(name = "crt_by_usr_nm", updatable = false)
private String createdByUsername;
#Column(name = "upd_by_usr_nm", insertable = false)
private String updatedByUsername;
}
AuditEntityListener (part of codes)
#Slf4j
#Component
public class AuditEntityListener {
#PrePersist
public void prePersist(AuditEntity auditEntity) {
log.trace("prePersist auditEntity:" + auditEntity);
auditEntity.setCreatedByUsername(requestSynchronizationManager.getLegalName());
}
}
During save Customer only, it works as charm, but if save CorporateCustomer, I got the error my BaseEntity property is null (hit null-constraint). Sts_cd should be automatically setValue during #prePersist. Below is the error,
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "sts_cd" violates not-null constraint
Detail: Failing row contains (0f0a348f-8ae2-493e-905e-20a82e87a4ae, 6eeadc61-ba2d-4ca3-9e09-2730d061ff82, {"rmNameCd": "01", "gamUnitCd": "EB", "lamUnitCd": "EB", "rmName..., [{"custCompany": "ABC Company", "extRatingKMV": 10, "lastApprove..., {"primarySectorCd": "C", "primaryIndustryCd": "46221", "primaryR..., null, null, null, null, null, null, null, 0).
I debugged the code, the BaseEntityListener actually get triggered when creating new record using JPA saveAndFlush, but turn out hit this error. Anyone has idea on this?

Serialize/Deserialize generic types in Spring Cloud Kafka Streams

The main purpose is to read a stream from a topic, apply some transformations and then send two events to other topics. For that we are using Kstream.branch() function and using functional style programming. The code is:
Input POJO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class FooInput {
#JsonProperty("field1")
private String field1;
#JsonProperty("field2")
private String field2;
}
Output POJO:
#Getter
#Setter
#ToString
#EqualsAndHashCode
public class FooEvent<T> extends EventInfo {
#JsonProperty(value = "entity")
private T entity;
#Builder
private FooEvent(T entity, String eventId, OffsetDateTime eventTime, Action eventAction, String eventSourceSystem, String eventEntityName) {
super(eventId, eventTime, eventAction, eventSourceSystem, eventEntityName);
this.entity = entity;
}
public FooEvent() {
super();
}
}
#Setter
#Getter
#ToString
#AllArgsConstructor
#NoArgsConstructor
public abstract class EventInfo {
#JsonProperty(value = "eventId")
private String eventId;
#JsonProperty(value = "eventTime")
private OffsetDateTime eventTime;
#JsonProperty(value = "eventAction")
private Action eventAction;
#JsonProperty(value = "eventSourceSystem")
private String eventSourceSystem;
#JsonProperty(value = "eventEntityName")
private String eventEntityName;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Bar {
#JsonProperty("field1")
private String field1;
#JsonProperty("field2")
private String field2;
#JsonProperty("field3")
private String field3;
}
Processor function:
#Bean
public Function<KStream<String, FooInput>, KStream<String, FooEvent<Bar>>[]> process() {
Predicate<String, FooEvent<Bar>> predicate1=
(key, value) -> value.getEntity().getField1().equalsIgnoreCase("test1");
Predicate<String, FooEvent<Bar>> predicate2=
(key, value) -> value.getEntity().getField1().equalsIgnoreCase("test2");
return input -> {
input
...
.branch(predicate1, predicate2);
};
}
The binds are declared in appplication.properties:
Input:
spring.cloud.stream.bindings.process-in-0.destination=topic0
spring.cloud.stream.bindings.process-in-0.content-type=application/json
Output:
spring.cloud.stream.bindings.process-out-0.destination=topic1
spring.cloud.stream.bindings.process-out-0.content-type=application/json
spring.cloud.stream.bindings.process-out-1.destination=topic2
spring.cloud.stream.bindings.process-out-1.content-type=application/json
The issue is when the application evaluates the predicate. It appears that it tries to convert to FooEvent<Bar>. It converts the eventId, eventTime, eventAction, ... fields just fine but when it comes to the entity field (in this case Bar) it stores the values on a HashMap (instead of creating a new Bar object and setting the proper fields) which leads me to believe that Spring default Serde (JsonSerde) is doing something wrong. Any suggestions on how to solve generic types Serde problem in Kafka Streams?

Named Query with List of object as input using JPA named query

Getting below QueryExecutionRequestException when I try to excecute update Named query using JPA repository.
org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.company.farmer.entity.FarmProducts p set p.isDeleted=:isDeleted where p.productId IN (:productIdsList_0, :productIdsList_1)]; nested exception is java.lang.IllegalStateException: org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.company.farmer.entity.FarmProducts p set p.isDeleted=:isDeleted where p.productId IN (:productIdsList_0, :productIdsList_1)]
Code:
#Repository
public interface FarmProductRepository extends JpaRepository<FarmProducts, Long> {
void deleteProduct(#Param("isDeleted") String isDeleted, #Param("productIdsList") List<Long> productIdsList);
}
#Override
public String deleteProductAndCategory(long categoryId, FarmProductIdsDTO farmProductIds) {
farmProductRepository.deleteProduct(FarmerProductCategoryConstants.DELETE_YES_FLAG, farmProductIds.getFarmProductIds());
return FarmerProductCategoryConstants.SUCCESS;
}
#Entity
#Table(name="farm_products")
#Getter
#Setter
#ToString(exclude= "productCategory")
#NoArgsConstructor
#AllArgsConstructor
#NamedQueries({#NamedQuery(name="FarmProducts.deleteProduct", query="update FarmProducts p set p.isDeleted=:isDeleted where p.productId IN (:productIdsList)") })
public class FarmProducts extends BaseModel {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "FARM_PRODUCT_GENERATOR")
#SequenceGenerator(name = "FARM_PRODUCT_GENERATOR", sequenceName = "FARM_PRODUCT_GENERATOR_SEQ", allocationSize = 1)
#Column(name = "farm_product_id")
public Long productId;
#ManyToOne
#JoinColumn(name = "farm_product_category_id")
#JsonIgnoreProperties("products")
public ProductCategory productCategory;
#Column(name = "product_name")
public String product;
#Column(name = "is_deleted")
public String isDeleted;
}
I am trying to pass a list of productId to make the isDeleted as "N". But update functionality fails because of the QueryExecutionRequestException.
By default Spring Data treats all queries as SELECT statements. If you have an UPDATE (or DELETE) statement you have you have to apply de #Modifying annotation on the query.
See https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.modifying-queries

JPA how to group by a collection property

Is there a way to group by a collection property? For example,
public class Merchandise {
id,
name
}
public class Attribute {
id,
name,
value,
#ManyToOne
MerchandiseCost merchandiseCost;
}
public class MerchandiseCost {
Merchandise merchandise,
List<Attribute> attributes,
BigDecimal cost,
}
Search MerchandiseCost group by merchandise and attributes.
select merchandise, attributes, sum(cost) from MerchandiseCost group by merchandise, attributes.
Will this be going to work?
EDIT:
If not, how to build a query to get results as following using CriteriaQuery API:
Merchandise Attributes SUM(COST)
-----------------------------------------------------------
Cloth size:L, color:RED 10000
Cloth size:M, color:WHITE 20000
Computer Memory:4G 80000
Computer Memory:16G 90000
You can not group by a collection and cannot select multi-valued field in the Select clause.
Merchandise.class
#Data
#Embeddable
#NoArgsConstructor
#EqualsAndHashCode
public class Merchandise {
private String name;
}
Attribute.class
#Data
#Embeddable
#EqualsAndHashCode
public class Attribute {
private int id;
private String name;
private String value;
private MerchandiseCost merchandiseCost;
#ManyToOne
public MerchandiseCost getMerchandiseCost() {
return merchandiseCost;
}
}
MerchandiseCost.class
#Data
#Entity
#EqualsAndHashCode
public class MerchandiseCost extends ABaseEntity {
private Merchandise merchandise;
private List<Attribute> attributes;
private BigDecimal cost;
#Embedded
public Merchandise getMerchandise() {
return merchandise;
}
public void setMerchandise(Merchandise merchandise) {
this.merchandise = merchandise;
}
#ElementCollection
#CollectionTable(name = "MERCHANDISE_ATTRIBUTE", joinColumns = #JoinColumn(name = "MERCHANDISE_ID"))
public List<Attribute> getAttributes() {
return attributes;
}
}
MerchandiseResult.class
#Data
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class MerchandiseResult {
private Merchandise merchandise;
private Attribute attribute;
private BigDecimal cost;
}
MerchandiseDao.class
#Stateless
public class MerchandiseDao {
#PersistenceContext(name = "tngo")
private EntityManager entityManager;
public void readCost(){
Query query = entityManager.createQuery("select NEW tngo.cert.training.model.MerchandiseResult(mc.merchandise, att, sum(mc.cost)) from MerchandiseCost mc join mc.attributes att group by mc.merchandise, att");
query.getResultList();
}
}

composite primary key which contains a foreign key

I have an entity called UserWithRoles:
#Entity
#Getter
#Setter
public class UserWithRoles implements Serializable
{
#Id
#GeneratedValue( strategy = GenerationType.AUTO )
private int id;
private String name;
private String password;
#OneToMany( mappedBy = "user" )
private List<UserRole> roles;
}
A UserRole entity:
#Entity
#Getter
#Setter
#IdClass( UserRolePK.class )
#Inheritance( strategy = InheritanceType.JOINED )
#DiscriminatorColumn( name = "roleType", discriminatorType = DiscriminatorType.STRING, length = 10 )
abstract public class UserRole implements Serializable
{
#Id
// It should be mapped as a foreign PK by user.id (user field declared below)
private int userID;
#Id
private String roleType;
#ManyToOne
#JoinColumn( name="user_id", referencedColumnName = "id" )
private UserWithRoles user;
}
The primary key class UserRolePK:
#Data
public class UserRolePK implements Serializable
{
private int userID;
private String roleType;
}
I want to create a composite PK to UserRole: UserWithRoles.id + UserRole.roleType
How can I map it to the database? Should I use the UserWithRoles type in the PK class instead of the ID? Is it a good idea at all? Or I just should use normal PK to UserRole? The relation would be something like that between the ClientOrder and ClientOrdetItem entities: (ClientOrder.id + ClientOrderItem.num)
You are using Derived Identity.
You need to change UserRole to look like this:
#Entity
#Getter
#Setter
#IdClass( UserRolePK.class )
#Inheritance( strategy = InheritanceType.JOINED )
#DiscriminatorColumn( name = "roleType", discriminatorType = DiscriminatorType.STRING, length = 10 )
abstract public class UserRole implements Serializable
{
#Id
private String roleType;
#Id
#ManyToOne
#JoinColumn( name="user_id", referencedColumnName = "id" )
private UserWithRoles user;
}
That is, get rid of the userID field and add an #Id annotation to the user field.
And change UserRolePK to look like this:
#Data
public class UserRolePK implements Serializable
{
private int user;
private String roleType;
}
That is, change the name of the userID field to user, to match the name of the #Id field in UserRole (but its type must still match the type of the UserWithRoles PK field, id).
Derived identity is discussed in JPA 2.1 spec, section 2.4.1.