In my OpenXava application, the #Calculation annotation does not work.
Here my coding for my #Embeddable that uses #Calculation:
import java.math.*;
import java.time.*;
import javax.persistence.*;
import org.openxava.annotations.*;
import lombok.*;
#Getter #Setter
#Embeddable
public class Payment {
#ManyToOne(fetch=FetchType.EAGER)
#DescriptionsList
Paymentfrequency paymentFrequency;
LocalDate firstPaymentDate;
#Stereotype("MONEY")
BigDecimal paymentAmount;
#ManyToOne(fetch=FetchType.LAZY)
#DescriptionsList
Methodofpayment methodOfPayment;
#ReadOnly
#Stereotype("MONEY")
#Calculation("paymentAmount * paymentFrequency.frequencyPerYear")
BigDecimal annualContribution;
}
And this the code for the entity with the collection of embeddables:
import javax.persistence.*;
import lombok.*;
#Entity #Getter #Setter
public class Paymentfrequency extends GenericType {
int frequencyPerYear;
// Payment is used as collection
#ElementCollection
#ListProperties("firstPaymentDate, paymentAmount, paymentFrequency,
methodOfPayment, annualContribution")
Collection<Payment> payments;
}
And this the result:
Note as the last column (annualContribution) is not recalculated when the operands change.
Why does not work #Calculation in this case?
#Calculation only works if all the operands are displayed in the user interface. In your case paymentFrequency.frequencyPerYear is not displayed given paymentFrequency is a reference displayed as #DescriptionsList.
Don't worry, just use a regular Java calculated property instead. In this way:
#Stereotype("MONEY")
#Depends("paymentAmount, paymentFrequency.id")
public BigDecimal getAnnualContribution() {
// You should refine the below code to lead with nulls
return getPaymentAmount().multiply(getPaymentFrequency().getFrequencyPerYear());
}
Learn more about calculated properties here:
https://openxava.org/OpenXavaDoc/docs/basic-business-logic_en.html
Related
I want to store the last read time in a Date column when ever someone tries to load or fetch(select query, findBy methods) a entity
I had a look at #PostLoad as explained in this site . Have following questions :-
(a)Just want to know if this is safe to Update entity in #PostLoad
(b) Is there something like #LastAcessedTime in JPA similar to #UpdateTimestamp ?
import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.time.LocalDate;
#Data
#Entity
public class SensitiveInfo {
#Id
private Long id;
private String data;
#CreationTimestamp
private LocalDate createdDate;
#UpdateTimestamp
private LocalDate updationTimeStamp;
// Is there a JPA automatic way to Update this field any time hibernate loads this entity ?
private LocalDate updationTimeStamp;
}
I have an entity Issue with a reference to an entity Priority. I want in the list mode of my OpenXava module, a different color and visual style for each row depending on the value of priority.
This is the code for my Issue entity:
package com.yourcompany.tracker.model;
import java.time.*;
import javax.persistence.*;
import org.openxava.annotations.*;
import org.openxava.calculators.*;
import org.openxava.model.*;
import lombok.*;
#Entity #Getter #Setter
public class Issue extends Identifiable {
#Column(length=100) #Required
String title;
#Stereotype("SIMPLE_HTML_TEXT")
String description;
#ReadOnly
#DefaultValueCalculator(CurrentLocalDateCalculator.class)
LocalDate createdOn;
#ManyToOne(fetch=FetchType.LAZY, optional=true)
#DescriptionsList
Priority priority;
}
And this for Priority:
package com.yourcompany.tracker.model;
import javax.persistence.*;
import javax.validation.constraints.*;
import org.openxava.annotations.*;
import lombok.*;
#Entity #Getter #Setter
public class Priority {
#Id #Max(9)
int level;
#Column(length=40) #Required
String description;
}
And this the effect I would like to achieve:
Note as rows with LOW priority are gray, and rows with HIGH priority are bold.
I know that there is a #RowStyle annotation, but it seems that it is for simple properties. How can I define a different style for each row depending on a reference (#ManyToOne) value?
#RowStyle allows you to use qualified properties, that is properties of a reference. Therefore, you can use #RowStyle inside a #Tab annotation in your entity, in this way:
#Entity #Getter #Setter
#Tab(
rowStyles= {
#RowStyle(style="high-priority", property="priority.level", value="7"),
#RowStyle(style="low-priority", property="priority.level", value="3"),
}
)
public class Issue extends Identifiable {
Note how for property attribute in #RowStyle we use "priority.level", so we reference to a property of the priority reference. Like stated above when priority.level is 7 we apply the high-priority style and when it is 3 we apply low-priority. These styles are CSS classes defined in your custom.css file, thus:
.high-priority {
font-weight: bold;
}
.low-priority {
color: lightgray !important;
}
To learn more read the OpenXava reference guide about #RowStyle.
I need to convert entity's field on fetch and according to this official example I've tried to do that with custom setter:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
#Table("entity")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(builderClassName = "EntityBuilder")
public class Entity {
#Id
private String someId;
#Transient
private String entityName;
#Column("entity_name")
public String getEntityNameUnmodified() {
return this.entityName;
}
#Column("entity_name")
public void setEntityNameUnmodified(String em) {
this.entityName = em + " Some modification";
}
}
But this completely doesn't work and as a result I get Entity with entityName == null.
I've downloaded GitHub example and run it locally and everything worked. What's wrong with my code?
My code didn't work because of missing #AccessType(AccessType.Type.PROPERTY) annotation.
Working solution:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.AccessType;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
#Table("entity")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(builderClassName = "EntityBuilder")
#AccessType(AccessType.Type.PROPERTY) // IMPORTANT !!!
public class Entity {
#Id
private String someId;
#Transient
private String entityName;
#Column("entity_name")
public String getEntityNameUnmodified() {
return this.entityName;
}
#Column("entity_name")
public void setEntityNameUnmodified(String em) {
this.entityName = em + " Some modification";
}
}
The thing is that Spring Data JDBC uses fields as accessors for entity's columns by default and without that annotation setters and getters are ignored. Enabling PROPERTY access type solves the problem.
I am getting following problem when trying to save an Entity into MongoDB database.
I am using Spring CrudRepository
An my code looks as follow:
UserDocument user = processUser();
userRepository.save(user);
This is the error I am getting:
java.lang.IllegalStateException: Could not obtain identifier from UserDocument(id=null, ownerId=..., ...)!
at o.s.d.m.TargetAwareIdentifierAccessor.getRequiredIdentifier(TargetAwareIdentifierAccessor.java:47)
at o.s.d.m.c.EntityOperations$MappedEntity.getId(EntityOperations.java:466)
... 53 frames excluded
UserDocument class:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.CompoundIndexes;
import org.springframework.data.mongodb.core.mapping.Document;
#Data
#EqualsAndHashCode(callSuper = true)
#NoArgsConstructor
#AllArgsConstructor
#Accessors(chain = true)
#SuperBuilder
#Document(collection = UserDocument.COLLECTION)
public class UserDocument extends BaseDocument<ObjectId> {
public static final String COLLECTION = "users";
#Id
private ObjectId id;
.....
}
For anyone that is struggling with this problem - in my case it was problem with mapstruct Mapper that as a side effect was populating fields in super class:
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.Version;
#Data
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#AllArgsConstructor(access = AccessLevel.PROTECTED)
#Accessors(chain = true)
#SuperBuilder
public abstract class BaseDocument<ID extends Serializable> implements Persistable<ID> {
#Version
private Long revision;
#CreatedDate
private Instant createdDateTime;
#LastModifiedDate
private Instant lastModifiedDateTime;
#Override
public boolean isNew() {
return isNull(createdDateTime);
}
}
So make sure these fields are null when you are saving a new entity!
I struggled with this too. In my case, the problem was the #Version field in the object I was trying to save was set to 0. After I set it to null I didn't have this anymore.
I noticed the methods in the trace getQueryForVersion, doSaveVersioned...
java.lang.IllegalStateException: Could not obtain identifier from .....
at org.springframework.data.mapping.TargetAwareIdentifierAccessor.getRequiredIdentifier(TargetAwareIdentifierAccessor.java:48)
at org.springframework.data.mongodb.core.EntityOperations$MappedEntity.getId(EntityOperations.java:527)
at org.springframework.data.mongodb.core.EntityOperations$MappedEntity.getQueryForVersion(EntityOperations.java:556)
at org.springframework.data.mongodb.core.MongoTemplate.doSaveVersioned(MongoTemplate.java:1383)
at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:1370)
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:88)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
I'm curious about the following problem. I've two entites, A and B. It stores almost the same information (for example, a name - in real life it's more complex), but the joins, and the foreign keys differs.
Can I do a mapped superclass, without an Id. And class A and class B, extending the mapped superclass, containing only the Id attribute?
For example:
import lombok.Getter;
import lombok.Setter;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
#MappedSuperclass
#Getter
#Setter
#Data
class superClass {
#Column(name = "name")
private String name;
}
#Entity
#Table(name = "A")
#Data
class A extends superClass {
#Id
#OneToMany
#JoinColumn(name = "id", referencedColumnName = "referencedName")
private SomeClass id;
}
#Entity
#Table(name = "B")
#Data
class B extends superClass {
#Id
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "referencedName")
private SomeOtherClass id;
}
Would it be valid by the JPA? I did read the mappedSuperClass's JavaDocs, and says nothing about it. I would said, that it is valid - but the IntelliJ Idea says, that the super class has to have an Id attribute. I didn't find anything on the internet about this.
edit: sorry, I missed it. I left the Entity annotation on the superClass, and that's why the Idea signed the error. I removed that, and the error disappeared. But I'm not sure, that this is valid, though.
yes, there is no requirement that a MappedSuperclass have anything in it. It is just providing extra annotations for the subclasses.
Yes it is valid. Anyway your superclass will not appear as a table in the DB.