How to extend Panache with static method? - quarkus

Hello. I want to write a static method in class with #MapperdSupperclass. such as findByName. so the child object can use method to query itself.
But I got error. It will query BaseUpdatableEntity not the childObject.The code like this.
#MappedSuperclass
#Slf4j
#ToString
public abstract class BaseUpdatableEntity extends BaseEntity {
#Getter
protected LocalDateTime created;
#Getter
protected LocalDateTime updated;
#Getter
protected String createdBy;
#Getter
protected String updatedBy;
#Version
#Setter
#Getter
protected Long lockVersion = 1L;
#Override
protected void prePersist() {
super.prePersist();
created = LocalDateTime.now();
createdBy = MDCScope.getUsername();
}
#PreUpdate
protected void preUpdate() {
updated = LocalDateTime.now();
updatedBy = MDCScope.getUsername();
}
#Override
public Object clone() throws CloneNotSupportedException {
BaseUpdatableEntity updatableEntity = (BaseUpdatableEntity) super.clone();
updatableEntity.setLockVersion(1L);
return updatableEntity;
}
public BaseUpdatableEntity getByName(String name) {
return find("name", name).<BaseUpdatableEntity>singleResultOptional().orElse(null);
}
}
so I read the PanacheEntityBase code. I find the static method with #GenerateBridge
But I don't konw how to archive it with myself?

Related

using model classes with association for serialization and deserialization with jackson

I have three classes with associations as follows:
Process.java
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
public class Process {
private UUID processId;
private List<User> users = new ArrayList<>();
private List<UnitType> units = new ArrayList<>();
private String furtherComment;
private List<AnsweredQuestionnaire> answeredQuestionnaires = new ArrayList<>()
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Process)) return false;
Process process = (Process) o;
return getProcessId().equals(process.getProcessId());
}
#Override
public int hashCode() {
return Objects.hash(getProcessId());
}
}
AnsweredQuestionnaire.java
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class AnsweredQuestionnaire {
private UUID answeredQuestionnaireId;
private Questionnaire questionnaire;
private Process process;
public void addProcessToAnsweredQuestionnaire(Process process){
//remove old association
if(this.process != null){
this.process.getAnsweredQuestionnaires().remove(this);
}
this.process = process;
//add new association
if(process != null){
this.process.getAnsweredQuestionnaires().add(this);
}
}
}
User.java
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class User {
private UUID userId;
private String firstName;
private String lastName;
private String phoneNumber;
private String email;
private List<Role> roles = new ArrayList<>();
private List<Process> processes = new ArrayList<>();
public void addProcessToUser(Process process){
this.processes.add(process);
process.getUsers().add(this);
}
public void removeProcessFromUser(Process process){
this.processes.remove(process);
process.getUsers().remove(this);
}
}
I have to use these classes for serialization and deserialization using objectMapper. I dont know the proper way to use the #JsonIdentityInfo annotation. Could someone please suggest me how to implement this. With the work done above I get circular dependency issues.
Use #JsonBackReference (on the List property) and #JsonManagedReference (on the object property) annotations for avoid the circular reference on serialization.

Spring boot -Axon framework NoHandlerForCommandException: No Handler for command

I am getting NoHandlerForCommandException: No Handler for command while trying to use Axon framework with Spring boot.
Below are my Java files :
The Rest controller ->
#RestController
#RequestMapping("/product")
public class ProductController {
#Autowired
private CommandGateway gateway;
#PostMapping
public ResponseEntity createProduct(#RequestBody CreateProductModel model) {
CreateProductCommand command=CreateProductCommand.builder()
.price("$123")
.productId(UUID.randomUUID().toString())
.product("Shoe")
.build();
String s=gateway.sendAndWait(command);
return new ResponseEntity<String>(HttpStatus.CREATED);
}
The ProductCreatedEvent object ->
import lombok.Data;
#Data
public class ProductCreatedEvent {
#TargetAggregateIdentifier
private String productId;
private String product;
private String price ;
}
The command class CreateProductCommand ->
#Builder
#Data
public class CreateProductCommand {
#TargetAggregateIdentifier
private final String productId;
private final String product;
private final String price ;
}
The Aggregate class ->
#Aggregate
public class ProductAggregate {
#AggregateIdentifier
private String productId;
private String product;
private String price ;
public ProductAggregate() {
}
#CommandHandler
public ProductAggregate(CreateProductCommand command) {
//TODO: Validation logic can be handled here
ProductCreatedEvent event=new ProductCreatedEvent();
BeanUtils.copyProperties(command, event);
AggregateLifecycle.apply(event);
}
#EventSourcingHandler
public void on(ProductCreatedEvent event) {
this.price=event.getPrice();
this.productId=event.getProductId();
this.product=event.getProduct();
}
}

Spring JPA Transaction ID

I have added an attribute to all my entities - transaction id - which is a sequence generated value that I bump up once in each transaction.
I also store the transaction id with user and start/end times so I have an audit trail for every change in the database.
What is the best way to handle storing a complete graph, where I basically only want to apply the transaction id to those entities that are actually dirty?
I can put a #PrePersist and #PreUpdate on the transaction id column, but how do I retrieve the value for the current transaction id? Is there a way to store and retrieve a value on the transaction object or other JPA controller? Do I need to use a ThreadLocal solution?
Ok, here is what I did. It seems to work in all of the use cases, though I have not done any performance testing, etc. If anyone sees anything that may be non-optimal or may fail in certain situations, please point it out.
Here is the base service class that all #Service implementations must extend:
public class BaseService
{
private final ActivityService activityService;
private final ApplicationEventPublisher applicationEventPublisher;
public static ThreadLocal<Activity> transaction = new ThreadLocal<>();
public BaseService(ActivityService activityService, ApplicationEventPublisher applicationEventPublisher)
{
this.activityService = activityService;
this.applicationEventPublisher = applicationEventPublisher;
}
Object executeWithinActivity(Updater updater)
{
boolean startedLocally = false;
try
{
if (transaction.get() == null)
{
startedLocally = true;
Activity activity = activityService.startTransaction();
transaction.set(activity);
}
return updater.execute(transaction.get());
}
finally
{
if (startedLocally)
{
applicationEventPublisher.publishEvent(new TransactionEvent());
Activity activity = transaction.get();
activityService.endTransaction(activity);
}
}
}
protected interface Updater
{
Object execute (Activity activity);
}
static class TransactionEvent
{
}
}
Activity is the entity that represents the stored transaction id:
#Entity
#Getter #Setter
#Table(name = "transactions", schema = "public", catalog = "euamdb")
public class Activity
{
#Id
#Column(name = "transaction_id", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tx_generator")
#SequenceGenerator(name = "tx_generator", sequenceName = "transaction_seq", allocationSize = 1)
private long transactionId;
#Basic
#Column(name = "user_id", length = 24)
private String userId;
#Basic
#Column(name = "transaction_start")
#CreationTimestamp
private Date transactionStart;
#Basic
#Column(name = "transaction_end")
#UpdateTimestamp
private Date transactionEnd;
#Override
public boolean equals(Object o)
{
if (this == o) return true;
if (!(o instanceof Activity)) return false;
Activity that = (Activity) o;
return transactionId == that.transactionId;
}
#Override
public int hashCode()
{
return Long.hashCode(transactionId);
}
}
ActivityService (which does not extend BaseService):
#Service
public class ActivityService
{
private final ActivityRepository activityRepository;
private final AuthUserService authService;
#Autowired
public ActivityService(ActivityRepository activityRepository, AuthUserService authService)
{
this.activityRepository = activityRepository;
this.authService = authService;
}
#Transactional
public Activity startTransaction()
{
Activity activity = new Activity();
activity.setTransactionStart(new Date());
activity.setUserId(authService.getAuthenticatedUserId());
activityRepository.save(activity);
return activity;
}
#Transactional
public void endTransaction(Activity activity)
{
activity.setTransactionEnd(new Date());
activityRepository.save(activity);
}
}
The base entity class for all entities (excepting Activity):
#MappedSuperclass
#Getter #Setter
public class BaseEntity
{
#Basic
#Column(name = "transaction_id")
private Long transactionId;
#PrePersist
#PreUpdate
public void setupTransaction ()
{
ThreadLocal<Activity> transaction = BaseService.transaction;
Activity activity = transaction.get();
long transactionId = activity.getTransactionId();
setTransactionId(transactionId);
}
}
An example of a service:
#Service
public class OrganizationService extends BaseService
{
private final OrgUserRepository orgUserRepository;
private final UserService userService;
#Autowired
public OrganizationService(ActivityService activityService,
OrgUserRepository orgUserRepository,
UserService userService,
ApplicationEventPublisher applicationEventPublisher)
{
super(activityService, applicationEventPublisher);
this.orgUserRepository = orgUserRepository;
this.userService = userService;
}
#Transactional
public OrgUser save(User user, OrgUser orgUser)
{
return (OrgUser) executeWithinActivity(activity ->
{
orgUser.setUser(userService.save(user));
return orgUserRepository.save(orgUser);
});
}
}
UserService also will extend BaseService and the save(OrgUser) method will also executeWithinActivity.
Finally, the commit listener:
#Component
public class AfterCommitListener
{
#TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void doAfterTxComplete(BaseService.TransactionEvent event)
{
BaseService.transaction.remove();
}
}

Redis - Why details are saving both as HASH and SET using Spring Data Redis?

I am new to Redis and developing Spring Boot + Spring Data Redis example. I am using CrudRepository, Example and ExampleMatchers API to do the searching from the Redis Key value store DB.
Now when I simply run my code, I saw that persons data saved as SET and HASH as well. Is this correct ? What's the use of saving the Person details both as SET and HASH
Showing all my code
public enum Gender {
MALE, FEMALE {
#Override
public String toString() {
return "Superwoman";
}
}
}
Species.java
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Species {
#Indexed
private String name;
}
Person.java
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
#RedisHash("persons")
public class Person {
#Id
private String id;
#Indexed
private String firstname;
private String lastname;
#Indexed
private Gender gender;
private List<String> nicknames;
#Indexed
private Integer age;
private Map<String, String> physicalAttributes;
#Reference
private Person relative;
private Species species;
}
PersonRepository.java
public interface PersonRepository extends CrudRepository<Person, String>, QueryByExampleExecutor<Person> {
}
RedisExampleDemoApplication.java
#SpringBootApplication
public class RedisExampleDemoApplication implements CommandLineRunner{
RedisMappingContext mappingContext = new RedisMappingContext();
ExampleQueryMapper mapper = new ExampleQueryMapper(mappingContext, new PathIndexResolver(mappingContext));
#Autowired
private PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(RedisExampleDemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Person person = Person.builder().firstname("Walter").gender(Gender.MALE).age(50).build();
Person person1 = Person.builder().firstname("Savani").gender(Gender.FEMALE).age(35).build();
personRepository.save(person);
personRepository.save(person1);
// [firstname:Walter, gender:MALE, age:50]
RedisOperationChain operationChain = mapper.getMappedExample(Example.of(person, ExampleMatcher.matchingAny()));
System.out.println(operationChain.getOrSismember());
System.out.println("----------------------------------------------");
Person p = Person.builder().lastname("Foo").build();
RedisOperationChain roc = mapper.getMappedExample(Example.of(p));
System.out.println(" == "+roc.getOrSismember());
System.out.println("-- "+roc.getSismember());
}
}
May be it is late to answer now , the reason that SET is visible is because of the secondary Index. I.e in your example First name is annotated as Indexed. Redis consider this as secondary index which is default a SET.

null values inserted while auditing

My AuditListener
public class EmployeeAuditListeners {
#PrePersist
public void prePersist(Employee employee){
perform(employee,Action.INSERTED);
}
#PreUpdate
public void preUpdate(Employee employee){
perform(employee,Action.UPDATED);
}
#PreRemove
public void preRemove(Employee employee){
perform(employee,Action.DELETED);
}
#Transactional
public void perform(Employee emp, Action action){
EntityManager em = BeanUtil.getBean(EntityManager.class);
CommonLogs commonLogs = new CommonLogs();
commonLogs.setQuery("new query");
em.persist(commonLogs);
}
}
and My Auditable.class
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class Auditable<U> {
#CreatedBy
protected U createdBy;
#CreatedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date createdDate;
#LastModifiedBy
protected U lastModifiedBy;
#LastModifiedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date lastModifiedDate;
}
My CommonLogs.class
#Entity
#EntityListeners(AuditingEntityListener.class)
public class CommonLogs extends Auditable<String> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String query;
public CommonLogs() {
}
public CommonLogs(String query) {
this.query = query;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}
My Employee.java class
#Entity
#EntityListeners(EmployeeAuditListeners.class)
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
and I have a simple Rest Controller
#RestController
#RequestMapping("/api")
public class EmployeeController {
#Autowired
private EmployeeRepository employeeRepository;
#PostMapping("/employees")
public Employee createEmployee(#RequestBody Employee employee){
return employeeRepository.save(employee);
}
}
I want to log it on my table (common_logs) every time i perform some crud operations on my Employee Entity.
the above given example is working to some extent as it successfully stores employee and invokes EmployeeAuditListeners.
but now while saving CommongLog entity i expect it's parent class Auditable to automatically insert createdBy, createdDate etc. for now only query and id is inserted on common_logs table and remaining columns are null.
You can review the documentation for Auditing in here.
To enable the automatic Auditing, you must add the annotation #EnableJpaAuditing in your Application class:
#SpringBootApplication
#EnableJpaAuditing
class Application {
static void main(String[] args) {
SpringApplication.run(Application.class, args)
}
}
If you want the fields #CreatedBy and #LastModifiedBy too, you will also need to implement the AuditorAware<T> interface. For example:
class SpringSecurityAuditorAware implements AuditorAware<User> {
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
return ((MyUserDetails) authentication.getPrincipal()).getUser();
}
}

Resources