Spring Data Jpa Publishing Events

We might come across a scenario where we need to publish certain events based on entity or domain object such as at the time of entity or domain object saved in the database. One way is to manually publish the entity on the queue. Or Spring Data Jpa provides a better way. All the entities or domain objects managed by Spring data jpa repositories are aggregate roots. This means Spring data Jpa provides a default listern for domain entity state change. In a Domain-Driven Design application, where we need to publish events based on state change. we can use these aggregate roots to publish events.

Spring Data provides @DomainEvents annotation and @AfterDomainEventPublication annotaion to mange the events.

drop database if exists AppleDb;
create database AppleDb;
use AppleDb;
create table apple ( apple_id bigint not null, apple_name varchar(255) default null, taste varchar(255) default null, primary key (apple_id)) engine=innodb;
create table hibernate_sequence (next_val bigint) engine=innodb;
insert into hibernate_sequence values ( 1 );
package com.sujan.example.jpa.controller;

import com.sujan.example.jpa.entity.Apple;
import com.sujan.example.jpa.repository.AppleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class AppleController {
    @Autowired
    private AppleRepository appleRepository;

    @PutMapping
    public List<Apple> save(@RequestBody List<Apple> apple) {
        return appleRepository.saveAll(apple);
    }
}
package com.sujan.example.jpa.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.domain.AfterDomainEventPublication;
import org.springframework.data.domain.DomainEvents;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Apple {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long appleId;
    private String appleName;
    private String taste;

    public Apple(String appleName, String taste) {
        this.appleName = appleName;
        this.taste = taste;
    }

    @DomainEvents
    Collection<Apple> domainEvents() {
        List<Apple> events = new ArrayList<Apple>();
        events.add(new Apple(this.appleId, this.appleName, this.taste));
        System.out.println("events = " + events);
        return events;
    }

    @AfterDomainEventPublication
    void callbackMethod() {
        System.out.println("Event Published");
    }
}
package com.sujan.example.jpa.repository;

import com.sujan.example.jpa.entity.Apple;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface AppleRepository extends JpaRepository<Apple, Long> {
}
package com.sujan.example.jpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class JpaApplication {
    public static void main(String[] args) {
        SpringApplication.run(JpaApplication.class, args);
    }
}

spring.datasource.url=jdbc:mysql://localhost:3306/AppleDb
spring.datasource.username=root
spring.datasource.password=root
plugins {
    id 'org.springframework.boot' version '2.3.3.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}
group = 'com.sujan'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}
repositories {
    mavenCentral()
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    annotationProcessor 'org.projectlombok:lombok'
    compileOnly 'org.projectlombok:lombok'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'mysql:mysql-connector-java'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}
test {
    useJUnitPlatform()
}
curl --location --request PUT 'http://localhost:8080/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "appleName": "Macintosh1",
    "taste": "sweet"
}'
select apple0_.apple_id as apple_id1_0_, apple0_.apple_name as apple_na2_0_, apple0_.taste as taste3_0_ from apple apple0_ where apple0_.apple_name=?

follow us on