Overview
I've got an #Entity with an #EmbeddedId composite key. The entities are exposed over a REST api which uses a BackendIdConverter to convert the id string back to an instance of the #Embeddable composite key. My understanding is that this is then used to identify an instance of the #Entity, but that isn't happening.
Question
How is the #Embeddable composite key resolved back to an #Entity?
Code
#Entity
public class MyEntity {
#EmbeddedId
private MyEntityIdentifier id;
#Embeddable
public class MyEntityIdentifier implements Serializable {
public static final String COMPOSITE_KEY_DELIMITER = "_#_";
#Column
private String idPartOne;
#Column
#Temporal(TemporalType.DATE)
private Date idPartTwo;
#Component
public class StringToMyEntityIdentifierConverter implements BackendIdConverter {
#Override
public Serializable fromRequestId(String id, Class<?> aClass) {
String[] split = id.split(COMPOSITE_KEY_DELIMITER);
String idPartOne = split[0];
Date idPartTwo = Date.valueOf(split[1]);
return new MyEntityIdentifier(fullName, lastUpdated);
}
public interface MyEntityRepository extends JpaRepository<MyEntity, MyEntityIdentifier> {
}
Related
I have entity class User with composite key via #IdClass annotation
#Entity
#IdClass(UserId.class)
public class User {
#Id
private String name;
#Id
private String surname;
private boolean active;
}
Composite key:
#Data
public class UserId implements Serializable {
private String name;
private String surname;
}
I use Spring Data with hibernate JPA under hood. Therefore I have repo interface:
public interface UserRepo extends JpaRepository<User, UserId> {
}
And I'd like to get all active users from db by list of concrete names and surnames.
E.g. I'd like to use method like this
List<User> findAllByActiveTrue(List<UserId> ids);
How can I do query for this requirement via Spring Data or JPQL?
You can use #Embeddable & #EmbeddedId for composite primary key
#Embeddable
public class UserId implements Serializable {
private String name;
private String surname;
}
#Entity
public class User {
#EmbeddedId
private UserId userId;
...
}
Then query like
List<User> findByUserIdInAndActiveTrue(List<UserId> ids);
A good tutorial about #Embeddable & #EmbeddedId here
I am attempting to load a customized value into the #CreatedBy field in my AuditFields entity. Whenever I make a POST request, I am expecting Spring Data Rest to use this AuditorAwareImpl and pass the TestPrincipal string to the #CreatedBy field.
The result I am actually getting is 409 Conflict, SQL Constraint created_by is null.
The BaseEntity needs to use #MappedSuperclass but I think it needs the functionality of #Entity in order to pass the value through to #CreatedBy?
Any pointers here would be greatly appreciated.
AuditEntity
#Embeddable
#Access(AccessType.FIELD)
#Data
#EntityListeners(AuditingEntityListener.class)
public class AuditFields implements Serializable {
#CreatedBy
#Column(name="CREATED_BY", length=256)
private String createdBy;
#Column(name="CREATED_TIMESTAMP")
private LocalDateTime createTimestamp = LocalDateTime.now();
...
...
}
BaseEntity
#MappedSuperclass
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class BaseEntity implements Serializable {
#Column(name="RECORD_STATUS_CODE", length=1)
#EqualsAndHashCode.Exclude
private String myStatus;
#Version
#Column(name="VERSION_NUMBER")
#Setter(AccessLevel.PROTECTED)
#EqualsAndHashCode.Exclude
private Long versionNumber;
#Embedded
#EqualsAndHashCode.Exclude
private AuditEntity auditFields;
}
AuditorAwareImpl
#Configuration
#EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class AuditorAwareImpl {
#Bean
public AuditorAware<String> auditorProvider() {
return new AuditorAware<String>() {
#Override
public Optional<String> getCurrentAuditor() {
return Optional.of("TestPrincipal");
}
};
}
}
The trick here is that #CreatedBy doesn't work in an #Embedded entity without some extra work.
I pieced together a few different tutorials, the result being that I put the #EntityListener in my MyEntity and wrote a custom listener class that uses #PrePost. You can then build it out more by adding a BaseEntity that embeds the AuditEntity, etc.
AuditEntity
#Embeddable
#Data
public class AuditEntity {
#Column(name="CREATED_BY")
#CreatedBy
private String createdBy;
// additional fields ...
}
MyEntity
#Data
#Table(name="TABLENAME", schema="SCHEMANAME")
#EntityListeners(CustomAuditListener.class)
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="MY_ID")
private Long myId;
#Embedded
#EqualsAndHashCode.Exclude
private AuditEntity auditFields;
}
CustomAuditListener
public class CustomAuditListener {
#PrePersist
public void prePersist(Object obj) {
MyEntity entity = (MyEntity) obj;
AuditFields audit = new AuditFields();
audit.setCreatedBy("CreatedByValueHere")
entity.setAuditFields(audit);
}
// can add #PreUpdate, etc, here
}
I inherited some pretty awful code that I am looking to refactor to make more reusable. There is a set of reporting tables which are primarily composed of 3 columns: id, report_type_fk, and report_description. I would like to merge all the reporting tables into one for ease of use.
I am refactoring the code and think that it would be better to break our current entities up so that Report is an abstract class with type implementations. For example a DmvReport extends Report, CreditScoreReport extends Report, etc.
The problem I am running into is that there would only be 1 report table that all entities would need to save to. Is there a way to make all concrete implementations of the abstract Report object save into the same table?
Here's an example of the bad code I inherited
Report class
#Entity
#Table(name = "report")
public class Report<E extends Exception> {
private long id;
private ReportType type;
private String description;
...
...
}
CreditReport class
#Entity
#Table(name = "credit_report")
public class CreditScore Report<E extends Exception> extends Report<E> {
private long id;
private ReportType type;
private String description;
...
...
}
I am looking to turn it into:
#MappedSuperclass
#Table(name = "report")
public abstract class Report<E extends Exception> {
#Id #Column(name="id")
private long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "report_type_id")
private ReportType type;
#column(name="description")
private String description;
...
...
}
#Entity
#Table(name = "report")
public class CreditScoreReport<E extends Exception> extends Report<E> {
public void doCreditScoreStuff(){
...
}
}
#Entity
#Table(name = "report")
public class DmvReport<E extends Exception> extends Report<E> {
public void doDmvStuff(){
...
}
}
I think you should use #Inheritance instead of #MappedSuperClass. Your code would look like this:
#Entity
#Table(name = "report")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "report_type_id", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Report<E extends Exception> {
#Id #Column(name="id")
private long id;
#column(name="description")
private String description;
...
...
}
#Entity(name = "CreditScoreReport")
#DiscriminatorValue("1") // the id corresponding to the credit score report
public class CreditScoreReport<E extends Exception> extends Report<E> {
#Column(name = "specific_credit_score_report_1)
private Integer specificCreditScoreReport1;
public void doCreditScoreStuff(){
...
}
}
#Entity(name = "DmvReport")
#DiscriminatorValue("2") // the id corresponding to the DMV report
public class DmvReport<E extends Exception> extends Report<E> {
#Column(name = "specific_dmv_score_report_1)
private Integer specificDmvScoreReport1;
public void doDmvStuff(){
...
}
}
This strategy allows you to store credit score report and DMV report data in one table (report), but instanciate the proper entity according to the report_value_id field. You don't have to define the report_value_id in your parameters because it was already used to create the required entity.
Is this what you're looking for?
I have a problem with abstract class where we implement an interface.
Now the interface is in our implementation of the abstract class other than the other implementation.
I'll show you the code here:
#MappedSuperclass
public abstract class AbstractOrder {
#Id
#GeneratedValue
private Long idOrder;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="datum")
private Date date = new Date();
#OneToMany(targetEntity=IOrderLine.class)
private List<IOrderLine> orderLines = new ArrayList<IOrderLine>();
private String refOrder;
...
}
#Entity
#Table(name="supplyorders")
public class SupplyOrder extends AbstractOrder implements Comparable<SupplyOrder>, Serializable {
#ManyToOne
private Supplier supplier;
#Enumerated(EnumType.STRING)
private SupplyOrderStatus status = SupplyOrderStatus.TOBESUPPLIED;
#ElementCollection
private Set<CustomerOrder> customerOrders = new HashSet<CustomerOrder>();
...
}
#Entity
#Table(name="customerorders")
public class CustomerOrder extends AbstractOrder implements commparable<CustomerOrder>,Serializable {
#ManyToOne
private Customer customer;
#ManyToOne
private Place place;
#ManyToOne
private User vendor;
private double deposit;
#Enumerated(EnumType.STRING)
private OrderStatus status = OrderStatus.CREATED;
}
#MappedSuperclass
public interface IOrderLine {
double getSubTotal();
int getQuantity();
Furniture getFurniture();
}
#Entity
#Table(name="supplyorderlines")
public class SupplyOrderLine implements IOrderLine, Serializable {
#Id
#GeneratedValue
private Long id;
#OneToMany
private List<CustomerOrderLine> customerOrderLines = new ArrayList<CustomerOrderLine>();
...
}
and of course a class CustormerOrderLine that implements IOrderLine.
Now for the supplyOrder they have supplyOrderLines in them and the customerOrder has the CustomerOrderLine in them.
The fault we get is that the abstract class doesn't know what implementation to take of the interface IOrderLine.
How can I override the field of orderLines from the abstract class in the implementation class and point to the implementation of the IOrderLine with annotations?
Thx in advance.
Chillworld
In Java you cannot instantiate an interface.
You can only instantiate actual classes that implement interfaces.
There's actually no such thing as IOrderLine.class.
You probably want to declare your orderLines field in your sub-classes.
In the sub-classes you can declare the field with a concrete class (that will map to a real database table).
If you need/want to use the abstract class to refer to your line items generically (this seems like a really good idea), you could use an abstract method that returns an interface.
Here's an example:
#MappedSuperclass
public abstract class AbstractOrder {
#Id
#GeneratedValue
private Long idOrder;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="datum")
private Date date = new Date();
// This abstract method will be implemented by sub-classes
public abstract List<IOrderLine> getOrderLines();
}
Then you can add the fields in your sub-classes and implement the abstract method that returns them:
#Entity
#Table(name = "supplyorderlines")
public class SupplyOrderLine implements IOrderLine, Serializable {
#Id
#GeneratedValue
private Long id;
#OneToMany(targetEntity = SupplyOrderLine.class)
private List<SupplyOrderLine> customerOrderLines;
#Override
public List<IOrderLine> getOrderLines() {
return customerOrderLines;
}
}
If this wasn't about JPA entities, you could also do something like this:
public abstract class PojoClass {
private Long idOrder;
private Date date = new Date();
private List<? extends IOrderLine> orderLines = new ArrayList<? extends IOrderLine>();
}
However, I don't think this is an option in a JPA entity.
That's because your entity class needs to map to a concrete class and database table.
I am trying to do mapping in JPA.
#Entity
public class Auction {
#Id
private Integer auctionId;
#OneToMany(mappedBy="auctionId")
#MapKey(name="auctionParamId")
private Map<AuctionParam, AuctionParamValue> values;
}
#Entity
public class AuctionParam {
#Id
private Integer auctionParamId;
private String description;
}
#Entity
public class AuctionParamValue {
#EmbeddedId
private AuctionParamValuePK pk;
private String value;
}
#Embeddable
public class AuctionParamValuePK {
#ManyToOne
#JoinColumn(name="auctionId")
private Auction auction;
#ManyToOne
#JoinColumn(name="auctionParamId")
private AuctionParam auctionParam;
}
Showing an error:-
.Error-Details:-Exception Description:
Entity [class
com.eaportal.domain.AuctionParamValue]
uses [class
com.eaportal.domain.AuctionParamValuePK]
as embedded id class
whose access-type
has been determined as [FIELD].
But
[class
com.eaportal.domain.AuctionParamValuePK]
does not define any [FIELD]. It is
likely that you have not provided
sufficient metadata in your id class
[class
com.eaportal.domain.AuctionParamValuePK].
If you come up with a solution please let me know.
Thanks in Advance
Tushar
You cannot use an EmbeddedId with relationships. Use an IdClass.
#Entity
#IdClass(AuctionParamValuePK.class)
public class AuctionParamValue {
#Id
#ManyToOne
#JoinColumn(name="auctionId")
private Auction auction;
#Id
#ManyToOne
#JoinColumn(name="auctionParamId")
private AuctionParam auctionParam;
#Basic
private String value;
}
public class AuctionParamValuePK {
private int auction;
private int auctionParam;
}
I think there are some errors in your Auction class. This is how I think it should look
#Entity
public class Auction {
#Id
private Integer auctionId;
#OneToMany(mappedBy="auction") // not auctionId
#MapKey(name="auctionParam") // not auctionParamId
private Map<AuctionParam, AuctionParamValue> values;
}
(The annotation values have to correspond with fields (or properties), not with columns)