I use #Audited annotation for my base model. I extend that for all my entities. but it not work. Is there any method I can use that
this is my base model
#MappedSuperclass
#Getter
#Setter
#Audited
public abstract class BaseModelObject implements Serializable {
/**
*
*/
private static final long serialVersionUID = 4194525198831057382L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
protected Long id;
}
This is my model class
#Entity
public class City extends BaseModelObject {
private static final long serialVersionUID = 1L;
#Column
private String name;
}
The #Audited annotation doesn't work the way you believe it should. By using it on a super class, it has no impact to the child classes which extend it, at least to control whether the child is or is not audited. This is by design.
Consider the notion where we have a superclass type and two different implementations, one which we want to audit with its superclass properties and one which we don't.
#MappedSuperclass
#Audited
public class Animal {}
#Entity
#Audited
public class Cat extends Animal {}
#Entity
public class Dog extends Animal {}
In this example, since #Audited isn't inherited, merely placing the annotation on the superclass and the Cat entity result in just Cat being audited. The Dog entity and its superclass property values are not.
If #Audited were treated as an inherited annotation, we'd have to introduce a series of #AuditOverride annotations to accomplish the same example, see below.
#MappedSuperclass
public class Animal {}
#Entity
#Audited
#AuditOverride(...)
public class Cat extends Animal {}
#Entity
public class Dog extends Animal {}
What makes this worse is if Animal had a subset of its properties audited, which would influence the number of #AuditOverrides.
This becomes even more complicated when you start to look at entity inheritance strategies and how those come into play with whether to audit an entity or not, and to what degree at what level of the hierarchy.
There is an entire discussion HHH-6331 and HHH-9770.
In short, if you want your child classes audited, they'll need to be explicitly annotated.
Try with this:
Superclass:
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class AuditableEntity implements Serializable {
private static final long serialVersionUID = 1L;
#CreatedDate
private LocalDateTime createdDate;
#LastModifiedDate
private LocalDateTime lastModifiedDate;
#CreatedBy
private String createdBy;
#LastModifiedBy
private String lastModifiedBy;
...
}
Entity class:
#Entity
public class City extends AuditableEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
Configuration:
#EnableJpaAuditing
#SpringBootApplication
#EnableTransactionManagement
#EntityScan("foo.entities")
#ComponentScan("foo")
#EnableJpaRepositories("foo.repositories")
public class ConfigApp {
...
}
Auditor service:
#Service
public class AuditorServiceImpl implements AuditorAware<String> {
#Override
public String getCurrentAuditor() {
return SecurityContextHolder.getContext().getAuthentication().getName();
}
}
Related
I have a problem with JPA inheritance. The database model is also specially built. It contains several tables with the same attributes (the tables were intentionally cut by country) and all these tables connect to another table (OneToOne).
Here is an example of the data model:
usa_user, germany_user, austria_user. All these tables have the same attributes (id, name, address). Now the address was also built up according to the countries e.g. usa_address, germany_address, austria_address.
Now I don't know or have the problem that I have been mapping them correctly for a long time. I have the following:
// All Lombok Getter, Setter Args,...
#MappedSuperclass
public abstract Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", referencedColumnName = "id")
#JsonIgnore
private User user;
private String name;
private String addr_num;
...
}
// All Lombok Getter, Setter Args,...
#MappedSuperclass
public abstract User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#JsonIgnore
private Address address;
private String name;
}
#Entity
#Table(name = "usa_user")
public class UsaUser extends User {}
#Entity
#Table(name = "austria_user")
public class AustriaUser extends User {}
#Entity
#Table(name = "germany_user")
public class GermanyUser extends User {}
#Entity
#Table(name = "usa_address")
public class UsaAddress extends Address {}
#Entity
#Table(name = "austria_address")
public class AustriaAddress extends Address {}
#Entity
#Table(name = "germany_address")
public class GermanyAddress extends Address {}
But unfortunately this does not work. Every time I start it JPA notices that it can't map the Entities Address - User (which is understandable because they are not entities but abstract classes). What would be the best way to solve this? I want to avoid that I have to list the attributes in all these entities because it would be redundant.
The goal is to find out how I can use a #MappedSuperclass in a #MappedSuperclass.
MappedSuperclass is not queryable and thus also not joinable. You need to map this as an abstract entity with the table per class inheritance strategy. Just switch to #Entity on the Address and User and add #Inheritance(TABLE_PER_CLASS).
I have an abtract class like below:
#MappedSuperclass
public abstract class BaseEntity {
#Id
Long id;
String name;
//getters and setters
}
and two entity extend BaseEntity
Fist class
#Entity
#Table(name= "table1")
public class TValideB extends BaseEntity {
#Column(name = "phone")
String phone;
}
Second class
#Entity
#Table(name= "table2")
public class TValide extends BaseEntity {
#Colmun(name = "mail")
String mail;
}
When i try to save TValide i get error like this non valid column "name";
In my table2 non column exsit for name.
My question is how can i ignore this column and save my entity?
Exist an other approaches without delete column name from abstract
class?
If you must use base classes, you can create two.
#MappedSuperclass
public abstract class BaseEntityWithId {
#Id
Long id;
//getters and setters
}
#MappedSuperclass
public abstract class BaseEntityWithName extends BaseEntityWithId {
String name;
//getters and setters
}
Then you simply have to pick the right one depending on the column layout of the table.
I just add #Column in name with insertable=false, updatable=false edit my abstract class like below:
#MappedSuperclass
public abstract class BaseEntity {
#Id
Long id;
#Column(name = "name", insertable=false, updatable=false)
String name;
//getters and setters
}
This approach avoid me to create a lot of abstract class or rewrite override attributes.
This should solve your problem.
In your class that extends BaseEntity, do this for the column that you don't want.
#Entity
#AttributeOverride(name = "name", column = #Column(name = "name", insertable = false, updatable = false)
public class TValide extends BaseEntity {
#Colmun(name = "mail")
String mail;
}
Change the way you use abstraction. Create a new abstract class that extends BaseEntity contains fields that are rarely used.
Example:
#MappedSuperclass
public abstract class BaseEntity {
#Id
Long id;
//getters and setters
}
#MappedSuperclass
public abstract class BaseNameEntity extends BaseEntity {
String name;
//getters and setters
}
#Entity
#Table(name= "table1")
public class TValideB extends BaseNameEntity {
#Column(name = "phone")
String phone;
//getters and setters
}
#Entity
#Table(name= "table2")
public class TValide extends BaseEntity {
#Column(name = "mail")
String mail;
//getters and setters
}
By doing this you can configure all your structure.
I have a super Entity class like this:
#Getter
#Setter
#NoArgsConstructor
public class GenericEntity {
#Id
private Long id;
#JsonIgnore
#CreatedBy
private Long createdBy;
#JsonIgnore
#CreatedDate
private Long createdDate;
#JsonIgnore
#LastModifiedBy
private Long updatedBy;
#JsonIgnore
#LastModifiedDate
private Long updatedDate;
#JsonIgnore
#Version
private Integer version = 0;
}
and a Role class extends from GenericEntity like this:
#Getter
#Setter
#NoArgsConstructor
public class Role extends GenericEntity {
private String name;
private String desc;
private Integer sort;
}
And after that I have interface RoleRepo like this:
#Repository
public interface RoleRepo extends ReactiveCrudRepository<Role, Long>;
In Router function, I have 2 handler methods
private Mono<ServerResponse> findAllHandler(ServerRequest request) {
return ok()
.contentType(MediaType.APPLICATION_JSON)
.body(roleRepo.findAll(), Role.class);
}
private Mono<ServerResponse> saveOrUpdateHandler(ServerRequest request) {
return ok()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(request.bodyToMono(Role.class).flatMap(role -> {
return roleRepo.save(role);
}), Role.class);
}
The method findAllHandler works fine, but the saveOrUpdateHandler throw exception like this:
java.lang.IllegalStateException: Required identifier property not found for class org.sky.entity.system.Role!
at org.springframework.data.mapping.PersistentEntity.getRequiredIdProperty(PersistentEntity.java:105) ~[spring-data-commons-2.2.0.M2.jar:2.2.0.M2]
at org.springframework.data.r2dbc.function.convert.MappingR2dbcConverter.lambda$populateIdIfNecessary$0(MappingR2dbcConverter.java:85) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
But when I move
#Id
private Long id;
from GenericEntity class to Role class, the two methods work fine.
Are there any Annations #MappedSuperclass/JPA in Spring Reactive Data like that
I wish the id field in GenericEntity for all extends class
Thanks for your help
Sorry, my English so bad
I had a similar problem and after some search, I didn't find an answer to your question, so I test it by writing code and the answer is spring data R2DBC doesn't need #Mappedsuperclass. it aggregates Role class properties with Generic class properties and then inserts all into the role table without the need to use any annotation.
I have
#MappedSuperclass
public class AbstractFoo implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
and I have an Entity inheriting / extending it:
#Entity
#Table(name = "Foo")
public class FooSomething extends AbstractFoo {
private static final long serialVersionUID = 1L;
#ManyToOne
#JoinColumn(name = "fk_something")
private Something something;
...
fk_something may be null.
How do I define the Entity Class that it should know to fetch only Foos where fk_something is not null?
I'm looking for something like this:
#Entity
#Table(name = "Foo")
#Query(SELECT *[fields of AbstractFoo + FooSomething] from <schema>.foo where fk_something is not null)
public class FooSomething extends AbstractFoo {
...
Whilst writing this question, I thought of a simple approach to simply define each time I fetch Foos via FooSomethingRepository to simply specify in query that "something" is not null.
Yet still I'm interested to know if there is a more formal way to define this on Entity Class level
Thank-you
I have a logic that saves some data and I use spring boot + spring data jpa.
Now, I have to save one object, and after moment, I have to save another objeect.
those of object consists of three primary key properties.
- partCode, setCode, itemCode.
let's say first object has a toString() returning below:
SetItem(partCode=10-001, setCode=04, itemCode=01-0021, qty=1.0, sortNo=2, item=null)
and the second object has a toString returning below:
SetItem(partCode=10-001, setCode=04, itemCode=01-0031, qty=1.0, sortNo=2, item=null)
there is a difference on itemCode value, and itemCode property is belonged to primary key, so the two objects are different each other.
but in my case, when I run the program, the webapp saves first object, and updates first object with second object value, not saving objects seperately.
(above image contains different values from this post question)
Here is my entity information:
/**
* The persistent class for the set_item database table.
*
*/
#Data
#DynamicInsert
#DynamicUpdate
#Entity
#ToString(includeFieldNames=true)
#Table(name="set_item")
#IdClass(SetGroupId.class)
public class SetItem extends BasicJpaModel<SetItemId> {
private static final long serialVersionUID = 1L;
#Id
#Column(name="PART_CODE")
private String partCode;
#Id
#Column(name="SET_CODE")
private String setCode;
#Id
#Column(name="ITEM_CODE")
private String itemCode;
private Double qty;
#Column(name="SORT_NO")
private int sortNo;
#Override
public SetItemId getId() {
if(BooleanUtils.ifNull(partCode, setCode, itemCode)){
return null;
}
return SetItemId.of(partCode, setCode, itemCode);
}
#ManyToMany(fetch=FetchType.LAZY)
#JoinColumns(value = {
#JoinColumn(name="PART_CODE", referencedColumnName="PART_CODE", insertable=false, updatable=false)
, #JoinColumn(name="ITEM_CODE", referencedColumnName="ITEM_CODE", insertable=false, updatable=false)
})
private List<Item> item;
}
So the question is,
how do I save objects separately which the objects' composite primary keys are partially same amongst them.
EDIT:
The entity extends below class:
#Setter
#Getter
#MappedSuperclass
#DynamicInsert
#DynamicUpdate
public abstract class BasicJpaModel<PK extends Serializable> implements Persistable<PK>, Serializable {
#Override
#JsonIgnore
public boolean isNew() {
return null == getId();
}
}
EDIT again: embeddable class.
after soneone points out embeddable class, I noticed there are only just two properties, it should be three of it. thank you.
#Data
#NoArgsConstructor
#RequiredArgsConstructor(staticName="of")
#Embeddable
public class SetGroupId implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#NonNull
private String partCode;
#NonNull
private String setCode;
}
Check howto use #EmbeddedId & #Embeddable (update you might need to use AttributeOverrides in id field, not sure if Columns in #Embeddable works).
You could create class annotated #Embeddable and add all those three ID fields there.
#Embeddable
public class MyId {
private String partCode;
private String setCode;
private String itemCode;
}
Add needed getters & setters.
Then set in class SetItem this class to be the id like `#EmbeddedId´.
public class SetItem {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="partCode",
column=#Column(name="PART_CODE")),
#AttributeOverride(name="setCode",
column=#Column(name="SET_CODE"))
#AttributeOverride(name="itemCode",
column=#Column(name="ITEM_CODE"))
})
MyId id;
Check also Which annotation should I use: #IdClass or #EmbeddedId
Be sure to implement equals and hashCode in SetGroupId.
Can you provide that class?