<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ishan Soni</title>
    <description>The latest articles on DEV Community by Ishan Soni (@ishansoni22).</description>
    <link>https://dev.to/ishansoni22</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F893417%2Fad7e674b-aa73-4adf-8ed2-fd78acb145e5.jpg</url>
      <title>DEV Community: Ishan Soni</title>
      <link>https://dev.to/ishansoni22</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ishansoni22"/>
    <language>en</language>
    <item>
      <title>Postgres Partitioning and Spring Data JPA</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Wed, 20 Mar 2024 13:52:12 +0000</pubDate>
      <link>https://dev.to/ishansoni22/postgres-partitioning-and-spring-data-jpa-3a45</link>
      <guid>https://dev.to/ishansoni22/postgres-partitioning-and-spring-data-jpa-3a45</guid>
      <description>&lt;h3&gt;
  
  
  But first — Inheritance
&lt;/h3&gt;

&lt;p&gt;Inheritance in Postgres allows you to create a table that inherits from another table. This feature can be used to model hierarchical data within the database. A child table inherits all columns from the parent table but can also have additional columns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Example: Build a data model for cities and population data. Each country has many cities but only 1 capital. A common access pattern that your model should be able to support is — Get all capitals and their population data!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  One way to solve this problem:
&lt;/h4&gt;

&lt;p&gt;A common approach one could take is the following (add an is_capital column to your schema).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

create table cities (
 name varchar(100),
 country_iso_code varchar(10),
 population bigserial,
 is_capital boolean,
 primary key (name)
);

insert into cities (name, country_iso_code, population, is_capital) values
('New Delhi', 'IN', 10927986, true),
('Pune', 'IN', 3124458, false),
('Bangkok', 'TH', 5104476, true),
('Almaty', 'KZ', 1977011, false),
('Stockholm', 'SE', 1515017, true),
('Washington, D.C.', 'US', 689545, true);

select * from cities where is_capital = true;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcf52kxzmnnz59fnlnl4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcf52kxzmnnz59fnlnl4w.png" alt="select * from cities where is_capital = true;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Another way to solve this problem (using inheritance)
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

create table cities (
 name varchar(100),
 country_iso_code varchar(10),
 population bigserial,
 primary key (name)
);

create table capitals (
 -- You can define additional columns here if you want to
) inherits (cities);

insert into cities (name, country_iso_code, population) values
('Pune', 'IN', 3124458),
('Almaty', 'KZ', 1977011);


insert into capitals (name, country_iso_code, population) values
('New Delhi', 'IN', 10927986),
('Bangkok', 'TH', 5104476),
('Stockholm', 'SE', 1515017),
('Washington, D.C.', 'US', 689545);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A select * from cities returns data from all tables (base + inherited).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvn6yczf31brhuxfq9s1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvn6yczf31brhuxfq9s1.png" alt="select * from cities;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The explain command shows that it does a scan on both the tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnixd0ndy1tcfo6iyy6dw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnixd0ndy1tcfo6iyy6dw.png" alt="Iexplain select * from cities;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A select * from capitals returns data in the capitals table only.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqt4fuyhniboacm1wp7g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqt4fuyhniboacm1wp7g.png" alt="select * from capitals;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The explain command shows that it does a scan on the capitals table only, potentially improving query performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73h7tcviabzkq2y4kxr2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73h7tcviabzkq2y4kxr2.png" alt="explain select * from capitals"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Partitioning
&lt;/h3&gt;

&lt;p&gt;Partitioning in postgres is based on/backed by inheritance under the hood. Partitioning in PostgreSQL involves splitting a large table into smaller, more manageable tables, while still treating them as a single table. This can significantly improve performance for large datasets by reducing index size, enhancing query performance, and making bulk operations like loads and deletes more efficient. This can also become the base for your archival process.&lt;/p&gt;

&lt;p&gt;The idea is to &lt;strong&gt;partition/split a table by a partition key&lt;/strong&gt; and &lt;strong&gt;while querying, supply the partition key in the where clause&lt;/strong&gt; such that postgres only queries the required partition thereby improving query performance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Example: We have a portfolio management application. It stores EOD stock prices for a large number of securities. Over time, this table will become bulky and this can deteriorate performance. Also, how do you create an archiving strategy to move/delete old data from this table?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

create table security_prices (
 id varchar(255),
 security_id varchar(255),
 price numeric,
 business_date date
) partition by range (business_date);

-- Create partitioned tables. In postgres, this has to be done manually :(
-- Range (Inclusive, Exclusive)
create table security_prices_march_2024 partition of security_prices for values from ('2024-03-01') to ('2024-04-01');
create table security_prices_april_2024 partition of security_prices for values from ('2024-04-01') to ('2024-05-01');
create table security_prices_may_2024 partition of security_prices for values from ('2024-05-01') to ('2024-06-01');

insert into security_prices (id, security_id, price, business_date) values 
('1', 'IBM', 191.42, '2024-03-19'),
('2', 'MSFT', 419.27, '2024-03-19'),
('3', 'AAPL', 174.85, '2024-03-19'),
('4', 'AMZN', 175.29, '2024-03-19'),
('5', 'NVDA', 1000, '2024-04-01'),
('6', 'NVDA', 1050, '2024-05-01'),
('7', 'AMD', 200, '2024-04-15'),
('8', 'AMD', 215, '2024-05-15');


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A select * from security_prices returns data from all partitioned tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1u9v2ldqu00080jjdc4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1u9v2ldqu00080jjdc4.png" alt="select * from security_prices;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The explain command shows that it does a scan on all the tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kuiyl45swwudp625fgv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kuiyl45swwudp625fgv.png" alt="explain select * from security_prices;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, what if you mention the partition key in the where clause?&lt;/p&gt;

&lt;p&gt;A select * from security_prices where business_date = ‘2024–03–19’ returns the correct data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbs9b76pdq2w7ijmn1ex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbs9b76pdq2w7ijmn1ex.png" alt="select * from security_prices where business_date = ‘2024–03–19’"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, the explain command shows that it queried the march table only!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hsf1wiyzxrh0eddde3v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hsf1wiyzxrh0eddde3v.png" alt="explain select * from security_prices where business_date = ‘2024–03–19’"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The example above is an example of Range Partitioning — Partitioning your tables by ranges.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Now that you have separate tables, you could drop or move data safely from old tables (Archiving!)&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — In the above example, I have not defined a primary key. In PostgreSQL, if you do not explicitly define a PK for a table, then the table will not have a PK (lookup ctid in postgres to get more information on this). If you choose to add a PK to a partitioned table (which I suggest you should), the PK must include the partition key. In our case, since business_date is the partition key, if you were to add a PK, it would need to include business_date along with other columns to ensure uniqueness across all partitions. For instance, you could define a composite primary key that includes both id and business_date: CREATE TABLE security_prices (…, PRIMARY KEY (id, business_date);. In this case, you can create a composite key (which will include both Id and BusinessDate) using @ Embeddable and embed it in your entity using @ EmbeddedId (see — &lt;a href="https://www.baeldung.com/jpa-composite-primary-keys" rel="noopener noreferrer"&gt;Composite keys and JPA&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Now, let’s map this entity to our spring data jpa application:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

spring.application.name=security-service

spring.datasource.url=jdbc:postgresql://localhost:5432/security-service
spring.datasource.username=postgres
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=validate


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Entity(name = "security_prices")
@Data
@EqualsAndHashCode(of = {"id"})
public class SecurityPrice {

    @Id
    /*
    IMP - How do you map an entity with no primary key in spring data jpa? 
    JPA expects every entity to have an @Id column!
    You cannot use the internal ctid postgres column here.
    But, you can use any business key as an Id in JPA even though it's not a primary key in the database
     */
    private String id = UUID.randomUUID().toString();
    private String securityId;
    private BigDecimal price;
    private LocalDate businessDate;

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Repository
public interface SecurityPriceJpaRepository extends JpaRepository&amp;lt;SecurityPrice, String&amp;gt; {
    Optional&amp;lt;SecurityPrice&amp;gt; findBySecurityIdAndBusinessDate(String securityId, LocalDate businessDate);
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Service
public class SecurityPriceService {

    private final SecurityPriceJpaRepository securityPriceJpaRepository;

    public SecurityPriceService(SecurityPriceJpaRepository securityPriceJpaRepository) {
        this.securityPriceJpaRepository = securityPriceJpaRepository;
    }

    @Transactional
    public String createSecurityPrice(SecurityPriceRequest securityPriceRequest) {
        String securityId = securityPriceRequest.securityId();
        LocalDate businessDate = securityPriceRequest.businessDate();

        Optional&amp;lt;SecurityPrice&amp;gt; existingPriceOptional =
                securityPriceJpaRepository.findBySecurityIdAndBusinessDate(securityId, businessDate);

        SecurityPrice securityPrice = new SecurityPrice();
        securityPrice.setSecurityId(securityId);
        securityPrice.setPrice(securityPriceRequest.price());
        securityPrice.setBusinessDate(businessDate);

        existingPriceOptional.ifPresent(price -&amp;gt; securityPrice.setId(price.getId()));

        securityPriceJpaRepository.save(securityPrice);
        return securityPrice.getId();
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public record SecurityPriceRequest(String securityId, BigDecimal price, LocalDate businessDate) { }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@RestController
@RequestMapping("/v1/securities")
public class SecurityPriceController {

    private final SecurityPriceService securityPriceService;

    public SecurityPriceController(SecurityPriceService securityPriceService) {
        this.securityPriceService = securityPriceService;
    }

    @PostMapping("/prices")
    public ResponseEntity&amp;lt;String&amp;gt; createPriceEntry(@RequestBody SecurityPriceRequest securityPriceRequest) {
        return ResponseEntity.ok(securityPriceService.createSecurityPrice(securityPriceRequest));
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6nj35ntc0hzi8xn6sz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6nj35ntc0hzi8xn6sz8.png" alt="Add a price"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqf6prywk8kwzxzc3owi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqf6prywk8kwzxzc3owi.png" alt="Price added"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the explain select * from security_prices with a business date of 2024–03–19 shows that it queries the march table only!&lt;/p&gt;

&lt;h3&gt;
  
  
  List Partitioning
&lt;/h3&gt;

&lt;p&gt;Another type of partitioning that is commonly used is List partitioning.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Example: You have an ecommerce application that generates a lot of orders. An order transitions through the following states — (CREATED, SHIPPED, DELIVERED, CANCELLED, ARCHIVED). A cron runs every night that marks DELIVERED (delivered &amp;gt; a month ago) and CANCELLED orders as ARCHIVED. Archived orders are only needed for audit/reporting purposes. Keeping these orders in the orders table will impact the performance of our application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s come up with a partitioning scheme for this problem:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

create table orders (
 order_id varchar(255),
 user_id varchar(255),
 total numeric,
 order_items jsonb,
 status varchar(255)
) partition by list (status);

create table active_orders partition of orders for values in ('CREATED', 'SHIPPED', 'DELIVERED', 'CANCELLED');

create table archived_orders partition of orders for values in ('ARCHIVED');


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let’s add a new order&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

insert into orders values ('1', '1', 100, null, 'CREATED');
select * from orders;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmepvoer6l454bo4xm8xf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmepvoer6l454bo4xm8xf.png" alt="select * from orders;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

explain select * from orders;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5x3s67s3r3nhe52fspcy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5x3s67s3r3nhe52fspcy.png" alt="explain select * from orders;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

explain select * from orders where status in ('CREATED', 'SHIPPED', 'DELIVERED', 'CANCELLED');


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1oyn9cro0jxpn5vne0r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1oyn9cro0jxpn5vne0r.png" alt="explain select * from orders where status in (‘CREATED’, ‘SHIPPED’, ‘DELIVERED’, ‘CANCELLED’);"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

update orders set status = 'ARCHIVED' where order_id = '1';


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The order will automatically move into the archived_orders table. In your application, always specify the status in the where clause. This way, your application will almost always access the active orders table which will have less numbers of orders at any given point of time boosting performance!&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>springdatajpa</category>
      <category>partitioning</category>
    </item>
    <item>
      <title>Introduction to Spring Cloud Function and Spring Cloud Stream</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Thu, 08 Feb 2024 17:21:20 +0000</pubDate>
      <link>https://dev.to/ishansoni22/introduction-to-spring-cloud-function-and-spring-cloud-stream-nnh</link>
      <guid>https://dev.to/ishansoni22/introduction-to-spring-cloud-function-and-spring-cloud-stream-nnh</guid>
      <description>&lt;h3&gt;
  
  
  What is Streaming and Stream processing?
&lt;/h3&gt;

&lt;p&gt;Event Stream Processing (&lt;strong&gt;ESP&lt;/strong&gt;) is the practice of taking action on a series of data points/events (stream) that originate from a system that continuously creates data.&lt;/p&gt;

&lt;p&gt;Stream processing can typically be represented using a DAG:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqfpoft01xmzbgnlpkq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqfpoft01xmzbgnlpkq8.png" alt="Stream Processing DAG" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a very high level abstraction. Can we model it using something we already have in Java? Yes — Java8 Functions!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupefrrpcalekjyf3d4uv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupefrrpcalekjyf3d4uv.png" alt="Stream Processing DAG — Java Functional Toolkit" width="800" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Spring Cloud Function
&lt;/h3&gt;

&lt;p&gt;Spring Cloud Function is a framework that promotes the implementation of business logic as Java Functions further instrumenting them to execute in context of different runtimes (HTTP, Messaging, etc.)!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt; — Let’s create a function that turns a string into uppercase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Function&amp;lt;String, String&amp;gt; toUpper() {
    return (str) -&amp;gt; {
        System.out.println("Original: " + str);
        return str.toUpperCase();
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are using spring initializer, and simply add the &lt;strong&gt;Spring Cloud Function&lt;/strong&gt; dependency, it will add the &lt;strong&gt;spring-cloud-function-context&lt;/strong&gt; dependency. If you have the &lt;strong&gt;Spring Web&lt;/strong&gt; dependency in your project and add the &lt;strong&gt;Spring Cloud Function&lt;/strong&gt; dependency, it will add the &lt;strong&gt;spring-cloud-function-web&lt;/strong&gt; dependency i.e. we will be able to expose the above function as a rest endpoint (i.e this function is automatically bound to an HTTP context). Let’s see it in action!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Returns a Function (A Processor node -&amp;gt; has both Input and Output!)
@Bean
public Function&amp;lt;String, String&amp;gt; toUpper() {
    return (str) -&amp;gt; {
        System.out.println("Original: " + str);
        return str.toUpperCase();
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start your application and invoke this function at:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;localhost:/toUpper/ishan&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2a22owc485t80c1h4rhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2a22owc485t80c1h4rhb.png" alt="Spring Cloud Function Output" width="800" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Spring Cloud Function, we were successfully able to bind this function to an HTTP context!&lt;/p&gt;

&lt;h3&gt;
  
  
  Spring Cloud Stream
&lt;/h3&gt;

&lt;p&gt;A framework that instruments your Spring Cloud Functions as event handlers bound to destinations using binders specific to a particular messaging system. Also see — &lt;a href="https://dev.to/ishansoni22/what-is-rabbitmq-and-why-do-you-need-a-message-broker-4onh"&gt;why do you need a messaging system?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eg — You want your Spring Cloud Function to become a message handler for a RabbitMQ destination(queue)!&lt;/p&gt;

&lt;h4&gt;
  
  
  Setup
&lt;/h4&gt;

&lt;p&gt;Add the &lt;strong&gt;spring-cloud-stream-binder-rabbit&lt;/strong&gt; dependency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23m301rzwmyekicvqcb3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23m301rzwmyekicvqcb3.png" alt="spring-cloud-stream-binder-rabbit dependency tree" width="800" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bring up RabbitMQ using docker-compose&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
version: “3.8”
services:
  rabbitmq:
    image: rabbitmq:3.11-management-alpine
    container_name: rabbitmq
    ports:
      - 5672:5672
      - 15672:15672
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest
      RABBITMQ_DEFAULT_VHOST: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnldqu8kyvpitra7b99qy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnldqu8kyvpitra7b99qy.png" alt="RabbitMQ before" width="800" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Connect RabbitMQ to your spring application (application.properties/yaml)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rabbitmq.host=localhost
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you bring the application up, you’ll see two new exchanges!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgk109tb2dien4soch8k2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgk109tb2dien4soch8k2.png" alt="RabbitMQ after" width="800" height="702"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you drop a message in toUpper-in-0 exchange, your toUpper function will get invoked and the output will be pushed to toUpper-out-0 exchange! Note — The REST access to this function will still work if you also have the spring-cloud-function-web dependency!&lt;/p&gt;

&lt;p&gt;Note — A &lt;strong&gt;Function&lt;/strong&gt; is a processor. That is why it created 2 exchanges! It listens to toUpper-in-0 and sends the output to toUpper-out-0&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let’s create a consumer for toUpper-out-0 exchange:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Bean
public Consumer&amp;lt;String&amp;gt; consumeUpper() {
    return (upper) -&amp;gt; {
        System.out.println("Consumed: " + upper);
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Spring Cloud Functions defined in your application
# Define multiple functions definitions by using ;
spring.cloud.function.definition=toUpper;consumeUpper

# Based on the Function definitions, Spring Cloud Stream will create bindings
# which connects the Function's input and/or output to destinations! 
# The toUpper-in-0 (the toUpper function listens to this)
# and toUpper-out-0 (the toUpper function produces results to this) were 
# automatically created

# If you do not do any configuration for the consumeUpper function,
# it will automatically create a consumeUpper-in-0 exchange and listen 
# to that. But we want this function to listen to toUpper-out-0
# What we are saying below is -&amp;gt;
# Bind consumeUpper-in-0 binding to listen to the toUpper-out-0 destination
# Since this is a Consumer function, the binding that is created is consumeUpper-in-0
spring.cloud.stream.bindings.consumeUpper-in-0.destination=toUpper-out-0
spring.cloud.stream.bindings.consumeUpper-in-0.group=school-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The concept of groups is similar to consumer groups in Kafka. But more on that in another article.&lt;/p&gt;

&lt;p&gt;Let’s drop the message “Ishan” into the exchange “toUpper-in-0” using the RabbitMQ admin console. You’ll see the following in your application logs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdcgcwhkn4gibl08vjui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdcgcwhkn4gibl08vjui.png" alt="From the toUpper function" width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7aygle81xb3bs1u5py8t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7aygle81xb3bs1u5py8t.png" alt="From the consumeUpper function" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Producer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can directly produce data to a destination using the StreamBridge class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Autowired
private StreamBridge streamBridge;

...

streamBridge.send(Destination, data);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or you can use a &lt;strong&gt;Supplier&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Supplier is a little different that a Function or a Consumer. Functions/Consumers are triggered whenever there is an input. But a Supplier doesn’t have a trigger. In Spring Cloud Function, the Supplier has an automated polling mechanism which polls the supplier every second (which you can override)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Bean
public Supplier&amp;lt;String&amp;gt; createData() {
    return () -&amp;gt; {
        System.out.println("Creating some data");
        return "Ishan-" +
                ThreadLocalRandom.current().nextInt(0, 10000) +
                "-" +
                LocalDateTime.now();
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring.cloud.function.definition=toUpper;consumeUpper;createData

...

# Modify the poll interval of the supplier
spring.cloud.stream.poller.fixed-delay=10000

# The binding destination of the supplier function.
# If we had provided no configuration, spring cloud stream would have
# automatically created a createData-out-0 exchange and the data this
# supplier creates will be pushed to that exchange. But we want this
# supplier to push data to the toUpper-in-0 exchange
spring.cloud.stream.bindings.createData-out-0.destination=toUpper-in-0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart your application again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8qtz878vse25gocn1jg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8qtz878vse25gocn1jg.png" alt="Output" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Sending and Consuming data based on specific binding and routing keys
&lt;/h4&gt;

&lt;p&gt;If you are unfamiliar with RabbitMQ routing and binding keys, &lt;a href="https://dev.to/ishansoni22/what-is-rabbitmq-and-why-do-you-need-a-message-broker-4onh"&gt;see this article first&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Example — You are using StreamBridge to send messages to a students exchange. How do you set the routing key of the message?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring.cloud.stream.rabbit.bindings.students.producer.routingKeyExpression=headers['type']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you are saying is, treat the type header as a routing key&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;

...

Message&amp;lt;StudentCreated&amp;gt; message = MessageBuilder.withPayload(studentCreated)
        .setHeader("type", "student.created").build();
streamBridge.send("students", message);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, how do you bind the below consumer and the queue it listens to a specific binding key on an exchange? (i.e this consumer only wants to listen to StudentCreated events from the students exchange)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Bean
public Consumer&amp;lt;StudentCreated&amp;gt; studentCreated() {
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring.cloud.function.definition=studentCreated
spring.cloud.stream.bindings.studentCreated-in-0.destination=students
spring.cloud.stream.bindings.studentCreated-in-0.group=school-service
#IMP
spring.cloud.stream.rabbit.bindings.studentCreated-in-0.consumer.bindingRoutingKey=student.created
spring.cloud.stream.rabbit.bindings.studentCreated-in-0.consumer.bindingRoutingKeyDelimiter=,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>spring</category>
      <category>springcloudfunction</category>
      <category>springcloudstream</category>
      <category>rabbitmq</category>
    </item>
    <item>
      <title>What is RabbitMQ and why do you need a Message Broker?</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Thu, 08 Feb 2024 14:31:32 +0000</pubDate>
      <link>https://dev.to/ishansoni22/what-is-rabbitmq-and-why-do-you-need-a-message-broker-4onh</link>
      <guid>https://dev.to/ishansoni22/what-is-rabbitmq-and-why-do-you-need-a-message-broker-4onh</guid>
      <description>&lt;h3&gt;
  
  
  Asynchronous communication and Message Brokers
&lt;/h3&gt;

&lt;p&gt;In traditional distributed applications, communication was usually done in a synchronous fashion. Synchronous communication, though simple has its own challenges.&lt;/p&gt;

&lt;p&gt;Let's take an example of an e-commerce application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctauzcyyey4pyulx0jlq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctauzcyyey4pyulx0jlq.png" alt="RPC communication in an ecommerce application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's assume our checkout service wants to interact with the inventory service to do a task. The following problems can occur if you are using synchronous communication:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There could be a network issue causing the RPC call to fail.&lt;/li&gt;
&lt;li&gt;The inventory service could be down.&lt;/li&gt;
&lt;li&gt;What if too many requests come in making the inventory service a bottleneck?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While you can create resiliency mechanisms for synchronous communication like retrying in case of network failures, introducing a circuit breaker to gracefully shut down functions in case the target service is down, spawning additional pods (horizontal scaling) and putting them behind a load balancer to distribute requests in case load on the system increases, there are still things that can go wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwdrpfxiyi8elunkqubc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwdrpfxiyi8elunkqubc.png" alt="Message Brokers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where asynchronous communication and message queues/brokers come into the picture. They sit in between distributed components.&lt;/p&gt;

&lt;p&gt;The checkout service will add an event/message to the message queue and move onto the next task. The inventory service, whenever it's ready (and at it’s own pace) will process these messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  RabbitMQ
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjjy1shd3dxijik4xp6t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjjy1shd3dxijik4xp6t.png" alt="RabbitMQ"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;RabbitMQ is a message broker and implements the AMQP (Advanced Messaging Queuing Protocol).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Producers&lt;/strong&gt;, instead of submitting messages directly to a queue, submit messages to an &lt;strong&gt;Exchange&lt;/strong&gt;. The message can have a &lt;strong&gt;Routing Key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Queues&lt;/strong&gt; are connected to exchanges using queue &lt;strong&gt;Bindings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consumers&lt;/strong&gt; listen to a queue.&lt;/p&gt;

&lt;p&gt;An Exchange routes an incoming message to queue(s) using the routing key of the incoming message and the queue bindings. How messages move through the system depends on the type of exchange.&lt;/p&gt;

&lt;h3&gt;
  
  
  Types of Exchanges
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fchqgdr3socg2kdntdu8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fchqgdr3socg2kdntdu8q.png" alt="Types of Exchanges"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Fanout Exchange
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku9f74c7ptzlscqvjmsf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku9f74c7ptzlscqvjmsf.png" alt="Fanout Exchange"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A fanout exchange routes messages to all queues that are bound to it and ignore the routing key.&lt;/p&gt;

&lt;h4&gt;
  
  
  Topic Exchange
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F86mgnu7n6059n0vv70zq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F86mgnu7n6059n0vv70zq.png" alt="Topic Exchange"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Topic exchanges route messages to queues based on wildcard matches between the routing key and the queue binding.&lt;/p&gt;

&lt;p&gt;The routing key must be a list of words, delimited by a period (.). Examples are agreements.us and agreements.eu.stockholm which in this case identifies agreements that are set up for a company with offices in lots of different locations. The routing patterns may contain an asterisk (“&lt;em&gt;”) to match a word in a specific position of the routing key (e.g., a routing pattern of “agreements.&lt;/em&gt;.&lt;em&gt;.b.&lt;/em&gt;” only match routing keys where the first word is “agreements” and the fourth word is “b”). A pound symbol (“#”) indicates a match of zero or more words (e.g., a routing pattern of “agreements.eu.berlin.#” matches any routing keys beginning with “agreements.eu.berlin”).&lt;/p&gt;

&lt;h4&gt;
  
  
  Direct Exchange
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8esnmrtbyty3rb9cqy0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8esnmrtbyty3rb9cqy0.png" alt="Direct Exchange"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the topic exchange, you can do a partial match between the routing key and queue bindings, while with a direct exchange you can only do a direct/full match.&lt;/p&gt;

</description>
      <category>rabbitmq</category>
      <category>asynchronous</category>
      <category>messagebroker</category>
    </item>
    <item>
      <title>Spring Data MongoDB — CRUD, Aggregations, Views and Materialized Views</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Tue, 30 Jan 2024 17:11:00 +0000</pubDate>
      <link>https://dev.to/ishansoni22/spring-data-mongodb-crud-aggregations-views-and-materialized-views-4bj7</link>
      <guid>https://dev.to/ishansoni22/spring-data-mongodb-crud-aggregations-views-and-materialized-views-4bj7</guid>
      <description>&lt;h3&gt;
  
  
  Basic Setup
&lt;/h3&gt;

&lt;p&gt;Setup a single node MongoDB instance using docker-compose.yaml and bring it up using the &lt;em&gt;docker-compose up -d&lt;/em&gt; command&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

version: '3.9'

services:
  mongo:
    image: mongo
    container_name: mongodb
    ports:
      - 27017:27017
    volumes:
      - ./mongo:/data/db
    environment:
      - MONGO_INITDB_ROOT_USERNAME=mongo
      - MONGO_INITDB_ROOT_PASSWORD=root


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I’d recommand using the MongoDB Compass application as the GUI tool.&lt;/p&gt;

&lt;p&gt;Add the following MongoDB dependencies in your pom.xml or use &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;spring initializer&lt;/a&gt; and select the &lt;strong&gt;Spring Data MongoDB&lt;/strong&gt; dependency:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-data-mongodb&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add the required data source configuration details to your application.properties/yaml files:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

spring.data.mongodb.authentication-database=admin #Pre-existing DB. For auth related stuff
spring.data.mongodb.username=mongo #See docker-compose.yml file
spring.data.mongodb.password=root  #See docker-compose.yml file
spring.data.mongodb.database=school-service #Name of your database
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Create your Entities and Repositories (Let’s create a school management project)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@ Document&lt;/strong&gt; annotation simply marks a class as being a domain object that needs to be persisted to the database, along with allowing us to choose the name of the collection to be used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@ Id&lt;/strong&gt; marks this attribute as the primary key. If the value of the @ Id field is not null, it’s stored in the database as-is; otherwise, the converter will assume we want to store an ObjectId in the database (either ObjectId, String or BigInteger work).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Address {
    //...Other fields
    private String city;
    private String state;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Document(value = "institutes")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Institute {
    @Id
    private String id;
    private String name;
    private Address address;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
    private String firstName;
    private String lastName;
    private Gender gender; //Enum - MALE, FEMALE
    private LocalDate dob;
    //Incremented using a cron that runs every day!
    private int age;
    private String email;
    private String phone;
    private Address address;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Hobby {
    private String hobby;
    //0 - 10
    private short experience;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Document("students")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Student {
    @Id
    private String id;
    private User userDetails;
    private String instituteId;
    private List&amp;lt;Hobby&amp;gt; hobbies;
    //...Other fields
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Document(value = "teachers")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Teacher {
    @Id
    private String id;
    private User userDetails;
    private String instituteId;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Document(value = "classes")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class InstituteClass {
    @Id
    private String id;
    private String instituteId;
    private String classId;
    private String section;
    private short year;
    private List&amp;lt;String&amp;gt; studentIds;
    private String classTeacherId; //Homeroom Teacher
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Repository
public interface InstituteRepository extends MongoRepository&amp;lt;Institute, String&amp;gt; {
    //A normal spring data query method.
    Optional&amp;lt;List&amp;lt;Institute&amp;gt;&amp;gt; findByName(String name);
}
//+ Other repositories


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add &lt;strong&gt;@ EnableMongoRepositories&lt;/strong&gt; to your application configuration. Spring will provide you with proxy repository implementations using which you can do almost all CRUD related stuff. Simply autowire these repositories and you are good to go:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febj8fq72jdb303qq3uth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febj8fq72jdb303qq3uth.png" alt="crud"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  User defined queries
&lt;/h3&gt;

&lt;p&gt;You can create user defined queries (and specify prototype — &lt;strong&gt;the where clause&lt;/strong&gt; and projection — &lt;strong&gt;the select clause&lt;/strong&gt;)&lt;/p&gt;

&lt;h4&gt;
  
  
  Using the @Query annotation:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Repository
public interface StudentRepository extends MongoRepository&amp;lt;Student, String&amp;gt; {

    // ?0 is a Positional parameter - will be replaced by id!
    // value -&amp;gt; Prototype and fields -&amp;gt; Projection!
    // Will only populate the id and hobbies fields in the student document!
    @Query(value = "{'id' : ?0}", fields = "{hobbies : 1}")
    Student findHobbiesById(String id);

    @Query(value = "{'userDetails.gender': ?0, 'userDetails.age': {'$lt': ?1}}")
    List&amp;lt;Student&amp;gt; findAllByGenderWithAgeLessThan(Gender gender, int age);

    @Query(value = "{'hobbies': {'$elemMatch': {'hobby': ?0, 'experience': {'$gt': ?1}}}}")
    List&amp;lt;Student&amp;gt; getStudentsWithHobbyExperienceGreaterThan(String hobby, int experience);

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Using MongoTemplate:
&lt;/h4&gt;

&lt;p&gt;Let’s try to re-create the above methods using MongoTemplate. You can simply auto-wire MongoTemplate in your repository classes and start using it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
...

@Repository
@Slf4j
public class CustomStudentRepository {

    private final MongoTemplate mongoTemplate;

    public CustomStudentRepository(MongoTemplate mongoTemplate) {
        log.info("Initialized mongodb template " + mongoTemplate);
        this.mongoTemplate = mongoTemplate;
    }

    public List&amp;lt;Student&amp;gt; findAllByGenderWithAgeLessThan(Gender gender, int age) {
        Query query = new Query();
        //query.allowSecondaryReads();
        //query.withReadPreference(...
        //query.withReadConcern(...

        Criteria criteria = Criteria.where("userDetails.gender").is(gender.name()).and("userDetails.age").lt(10);

        query.addCriteria(criteria);

        //mongoTemplate.find(Query, Entity.class);
        return mongoTemplate.find(query, Student.class);
    }


    public List&amp;lt;Student&amp;gt; getStudentsWithHobbyExperienceGreaterThan(String hobby, int experience) {
        Query query = new Query();
        Criteria criteria = Criteria.where("hobbies").elemMatch(
                Criteria.where("hobby").is(hobby)
                        .and("experience").gt(experience)
        );
        query.addCriteria(criteria);

        return mongoTemplate.find(query, Student.class);
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Updates
&lt;/h3&gt;

&lt;p&gt;Let’s assume you have a cron that runs at midnight and finds out all of the students whose birthday is today. We now need to increment their age by 1. Here is how you can do it using MongoTemplate:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

//Todo - Instead, use in and pass-in a list of student ids!
public void incrementAgeBy1(String studentId) {
    Query query = new Query();
    query.addCriteria(Criteria.where("id").is(studentId));

    Update update = new Update();
    //update.set()
    //update.unset()
    //update.addToSet()
    //update.push()
    //and more...
    update.inc("userDetails.age", 1);

    //mongoTemplate.updateFirst(Query, UpdateDefinition, Entity.class);
    mongoTemplate.updateFirst(query, update, Student.class);
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Aggregations
&lt;/h3&gt;

&lt;p&gt;Let’s try to do the following aggregations — &lt;strong&gt;Find gender count for an institute&lt;/strong&gt; and &lt;strong&gt;Find gender count for all institutes&lt;/strong&gt;:&lt;/p&gt;
&lt;h4&gt;
  
  
  Using the @Aggregation annotation:
&lt;/h4&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import org.springframework.data.mongodb.repository.Aggregation;
...

@Repository
public interface StudentRepository extends MongoRepository&amp;lt;Student, String&amp;gt; {
    //...Other methods

    record StudentsByGenderForInstitute(String gender, long total) {}

    @Aggregation(pipeline = {
            "{'$match': {'instituteId': ?0}}",
            "{'$group': {'_id': '$userDetails.gender', 'total': {'$count':{}}}}",
            "{'$project': {'_id': 0, 'gender': '$_id', 'total': 1}}"
    })
    List&amp;lt;StudentsByGenderForInstitute&amp;gt; getStudentsByGenderForInstitute(String instituteId);

    @Data
    class StudentsByGender {
        private String instituteId;
        private String gender;
        private long total;
    }

    @Aggregation(pipeline = {
            "{'$group': {'_id': {'instituteId: '$instituteId', 'gender': '$userDetails.gender'}, 'total': {'$count': {}}}}",
            "{'$project': {'_id': 0, 'instituteId': '$_id.instituteId', 'gender': '$_id.gender', 'total': 1}}"
    })
    //Not working for some reason - throws a stack overflow error
    List&amp;lt;StudentsByGender&amp;gt; getStudentsByGender();

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Let’s try to do the same aggregations using MongoTemplate:
&lt;/h4&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import org.springframework.data.mongodb.core.aggregation.*;
...

@Repository
@Slf4j
public class CustomStudentRepository {

    private final MongoTemplate mongoTemplate;

    public CustomStudentRepository(MongoTemplate mongoTemplate) {
        log.info("Initialized mongodb template " + mongoTemplate);
        this.mongoTemplate = mongoTemplate;
    }
    //...Other methods    

    record StudentsByGenderForInstitute(String gender, long total) {}

    public List&amp;lt;StudentsByGenderForInstitute&amp;gt; getStudentsByGenderForInstitute(String instituteId) {
        MatchOperation match = Aggregation.match(Criteria.where("instituteId").is(instituteId));
        GroupOperation group = Aggregation.group("userDetails.gender").count().as("total");
        ProjectionOperation project = Aggregation.project()
                .andExclude("_id")
                .and("_id").as("gender")
                .andInclude("total");

        Aggregation aggregation = Aggregation.newAggregation(match, group, project);

        AggregationResults&amp;lt;StudentsByGenderForInstitute&amp;gt; results =
                mongoTemplate.aggregate(aggregation, Student.class, StudentsByGenderForInstitute.class);
        return results.getMappedResults();
    }

    record StudentsByGender(String instituteId, String gender, long total) {}

    public List&amp;lt;StudentsByGender&amp;gt; getStudentsByGender() {
        GroupOperation group = Aggregation.group("instituteId", "userDetails.gender").count().as("total");

        ProjectionOperation project = Aggregation.project().andExclude("_id")
                .and("_id.instituteId").as("instituteId")
                .and("_id.gender").as("gender")
                .andInclude("total");

        Aggregation aggregation = Aggregation.newAggregation(group, project);
        AggregationResults&amp;lt;StudentsByGender&amp;gt; results =
                mongoTemplate.aggregate(aggregation, Student.class, StudentsByGender.class);
        return results.getMappedResults().stream()
                .sorted(Comparator.comparing(result -&amp;gt; result.instituteId)).collect(Collectors.toList());
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Views
&lt;/h3&gt;

&lt;p&gt;A MongoDB view is a &lt;strong&gt;read-only&lt;/strong&gt; queryable object whose contents are defined by an &lt;strong&gt;aggregation pipeline on other collections or views&lt;/strong&gt;. MongoDB does not persist the view contents to disk. A view’s content is computed on-demand when a client queries the view.&lt;/p&gt;

&lt;p&gt;Standard views use the indexes of the underlying collection. As a result, you cannot create, drop or re-build indexes on a standard view directly, nor get a list of indexes on the view.&lt;/p&gt;

&lt;p&gt;A view definition pipeline cannot include the $out or the $merge stage. This restriction also applies to embedded pipelines, such as pipelines used in $lookup or $facet stages.&lt;/p&gt;

&lt;p&gt;You can create a view/materialised view on any aggregation pipeline, but it’ll make the most sense if you are able to join multiple collections! — $lookup.&lt;/p&gt;
&lt;h4&gt;
  
  
  Lookup
&lt;/h4&gt;

&lt;p&gt;$lookup performs a &lt;strong&gt;left outer join&lt;/strong&gt; to a collection in the same database to filter in documents from the “joined” collection for processing. The $lookup stage adds a new array field to each input document. The new array field contains the matching documents from the “joined” collection. The $lookup stage passes these reshaped documents to the next stage. You’ll probably end up using &lt;strong&gt;$unwind&lt;/strong&gt; (Kind of like flatMap) with $lookup since the joined collections is added as an array field!&lt;/p&gt;
&lt;h4&gt;
  
  
  Example: Create a View of Students with Institute Details
&lt;/h4&gt;

&lt;p&gt;Let’s start with something:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

//Aggregation.lookup(from, localfield, foreignfield, as);   
LookupOperation lookup = Aggregation.lookup("institutes", "instituteId", "_id", "institute");    
AggregationResults&amp;lt;Object&amp;gt; results = mongoTemplate.aggregate(Aggregation.newAggregation(lookup), Student.class, Object.class);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The results are like this (notice the array):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzu0ifd3yd6ehvka1wfqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzu0ifd3yd6ehvka1wfqk.png" alt="lookup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s add the $unwind stage&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

LookupOperation lookup = Aggregation.lookup("institutes", "instituteId", "_id", "institute");
UnwindOperation unwind = Aggregation.unwind("institute");
AggregationResults&amp;lt;Object&amp;gt; results = mongoTemplate.aggregate(Aggregation.newAggregation(lookup, unwind), Student.class, Object.class);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fke5f4cq1c755lbcutrl2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fke5f4cq1c755lbcutrl2.png" alt="unwind"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s combine these operations and create our view:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

record StudentWithInstituteDetails(String _id, User userDetails, List&amp;lt;Hobby&amp;gt; hobbies, Institute institute) {}

@PostConstruct
public void createStudentsWithInstituteDetailsView() {
    LookupOperation lookup = Aggregation.lookup("institutes", "instituteId", "_id", "institute");
    UnwindOperation unwind = Aggregation.unwind("institute");
    AggregationOperation[] aggregationOperations = {lookup, unwind};
    mongoTemplate.createView("students-with-institute-details", Student.class, aggregationOperations);
}

public List&amp;lt;StudentWithInstituteDetails&amp;gt; getStudentsWithInstituteDetails() {
    return mongoTemplate.find(new Query(), StudentWithInstituteDetails.class, "students-with-institute-details");
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhow7aduhfuzymgmuk68b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhow7aduhfuzymgmuk68b.png" alt="our view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Materialized Views
&lt;/h3&gt;

&lt;p&gt;An on-demand materialized view is a &lt;strong&gt;pre-computed&lt;/strong&gt; aggregation pipeline result that is &lt;strong&gt;stored on and read from disk&lt;/strong&gt;. On-demand materialized views are typically the results of a $merge or $out stage. Materialized views are therefore &lt;strong&gt;much more performant&lt;/strong&gt; than standard views.&lt;/p&gt;

&lt;p&gt;$merge is preferred but make sure you think about how your materialised view will get refreshed when data updates happen. You don’t want your entire data to be refreshed every time a single data update happens!&lt;/p&gt;

&lt;h4&gt;
  
  
  Example — Create a materialised view for the following aggregation — Group students by school and gender
&lt;/h4&gt;

&lt;p&gt;Your initial run should cover all schools and all students, but when a student enrols or leaves a particular school, you want to rerun the pipeline for only that combination&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Materialised View =
{_id: {school: A, gender: Female}, count: 50}
{_id: {school: A, gender: Male}, count: 55}
{_id: {school: B, gender: Female}, count: 60}
{_id: {school: B, gender: Male}, count: 40}
...

Eg. a new female student joins school A
You only want the first document to be updated! (_id!)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@PostConstruct
public void createSchoolGenderView() throws Exception {
    LookupOperation lookup = Aggregation.lookup("institutes", "instituteId", "_id", "institute");
    UnwindOperation unwind = Aggregation.unwind("institute");
    GroupOperation group = Aggregation.group("institute.name", "userDetails.gender").count().as("count");
    MergeOperation merge = Aggregation.merge().intoCollection("school_gender_view").whenMatched(MergeOperation.WhenDocumentsMatch.replaceDocument()).build();

    mongoTemplate.aggregate(
            Aggregation.newAggregation(lookup, unwind, group, merge),
            Student.class,
            Object.class
    );
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;But how do I refresh this? You could create something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public void createSchoolGenderView(String instituteId, String gender) {
    MatchOperation match = null;
    if (StringUtils.hasLength(instituteId) &amp;amp;&amp;amp; StringUtils.hasLength(gender)) {
        match = Aggregation.match(
                Criteria.where("instituteId").is(instituteId)
                        .and("userDetails.gender").is(gender)
        );
    }

    LookupOperation lookup = Aggregation.lookup("institutes", "instituteId", "_id", "institute");
    UnwindOperation unwind = Aggregation.unwind("institute");
    GroupOperation group = Aggregation.group("institute.name", "userDetails.gender").count().as("count");
    MergeOperation merge = Aggregation.merge().intoCollection("school_gender_view").whenMatched(MergeOperation.WhenDocumentsMatch.replaceDocument()).build();
    Aggregation aggregation = null;
    if (Objects.nonNull(match)) {
        aggregation = Aggregation.newAggregation(match, lookup, unwind, group, merge);
    } else {
        aggregation = Aggregation.newAggregation(lookup, unwind, group, merge);
    }

    mongoTemplate.aggregate(
            aggregation,
            Student.class,
            Object.class
    );

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdol9ctzm7unws4kqdu8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdol9ctzm7unws4kqdu8m.png" alt="materialized view"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>springboot</category>
      <category>springdatamongodb</category>
      <category>materializedviews</category>
    </item>
    <item>
      <title>Java Multithreading — Thread states and introduction to thread profiling</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Thu, 04 Jan 2024 12:41:20 +0000</pubDate>
      <link>https://dev.to/ishansoni22/java-multithreading-thread-states-and-introduction-to-thread-profiling-52jo</link>
      <guid>https://dev.to/ishansoni22/java-multithreading-thread-states-and-introduction-to-thread-profiling-52jo</guid>
      <description>&lt;h3&gt;
  
  
  Thread states
&lt;/h3&gt;

&lt;p&gt;A Thread can be in any one of the following states at any point in time:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykudnrnip3gq3spnxwwe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykudnrnip3gq3spnxwwe.png" alt="Thread states"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  NEW
&lt;/h4&gt;

&lt;p&gt;This is the state of a thread when it is created but not yet started. For example, when you create a new Thread object using the constructor, the thread is in the new state. You can only call the start() method on a new thread; otherwise, an IllegalThreadStateException will be thrown.&lt;/p&gt;

&lt;h4&gt;
  
  
  RUNNABLE
&lt;/h4&gt;

&lt;p&gt;This is the state of a thread when it is ready to run and waiting for CPU time. For example, when you call the start() method on a new thread, or when a blocked or waiting thread becomes eligible to run again. The thread scheduler selects a thread from the runnable state for execution, based on its priority and other factors.&lt;/p&gt;

&lt;h4&gt;
  
  
  RUNNING
&lt;/h4&gt;

&lt;p&gt;This is the state of a thread when it is executing on the CPU. For example, when the thread scheduler picks a thread from the runnable state and assigns it to a CPU core, the thread is in the running state. The thread executes its run() method and performs its tasks. A thread can enter the running state only from the runnable state.&lt;/p&gt;

&lt;h4&gt;
  
  
  BLOCKED
&lt;/h4&gt;

&lt;p&gt;This is the state of a thread when it is waiting to acquire a monitor lock that is held by another thread. For example, when a thread tries to enter a synchronized block or method that is already occupied by another thread, the thread is blocked until the lock is released. A thread can enter the blocked state from the running state, and can return to the runnable state when it acquires the lock.&lt;/p&gt;

&lt;h4&gt;
  
  
  WAITING
&lt;/h4&gt;

&lt;p&gt;This is the state of a thread when it is waiting indefinitely for another thread to perform a certain action. For example, when a thread calls the wait() method on an object, or the join() method on another thread, or the park() method of LockSupport class, the thread is in the waiting state. A thread can enter the waiting state from the running state, and can return to the runnable state when it receives a notification or an interruption.&lt;/p&gt;

&lt;h4&gt;
  
  
  TIMED WAITING
&lt;/h4&gt;

&lt;p&gt;This is the state of a thread when it is waiting for a specified amount of time for another thread to perform a certain action. For example, when a thread calls the sleep() method, or the wait() method with a timeout parameter, or the join() method with a timeout parameter, or the parkNanos() or parkUntil() methods of LockSupport class, the thread is in the timed waiting state. A thread can enter the timed waiting state from the running state, and can return to the runnable state when the timeout expires or when it receives a notification or an interruption.&lt;/p&gt;

&lt;h4&gt;
  
  
  TERMINATED
&lt;/h4&gt;

&lt;p&gt;This is the state of a thread when it has completed its execution or has been aborted due to an exception or an error. For example, when a thread finishes its run() method normally or abnormally, or when it is stopped by calling the stop() method (which is deprecated), the thread is in the terminated state. A thread can enter the terminated state only from the running state, and cannot return to any other state.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is the state of a thread when it is waiting for an I/O operation to complete?&lt;/strong&gt; — It’s either Blocked or Timed Waiting, depending on the type of I/O operation and the API used. When a thread performs a traditional blocking I/O operation, such as reading a file or a socket, it enters the blocked state and waits for the I/O operation to finish. However, some I/O operations may have a timeout parameter that specifies how long the thread should wait for the operation to complete. In this case, the thread enters the timed waiting state and waits for either the operation to finish or the timeout to expire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can the OS put a thread executing a synchronised block into the blocked state mid way and schedule a blocked thread that is waiting for the lock?&lt;/strong&gt; — No. This is because the synchronized block ensures that only one thread can enter the block at a time, and the thread that enters the block acquires the monitor lock of the object that is used for synchronization. The OS cannot interrupt or preempt a thread that holds a monitor lock, unless the thread voluntarily releases the lock by exiting the block or by calling wait() on the object.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Thread Profiling
&lt;/h3&gt;

&lt;p&gt;Thread Profiling can help us in understanding what states threads are in and why. We can also see if the threads are able to run in parallel or not (i.e see the concurrency level of threads). It also tells us how much time threads spend in different states. We can also find and analyse cases of high lock contention. We’ll use Java Flight Recorder (JFR) to profile threads.&lt;br&gt;
What is Java Flight Recorder and how to enable it and do a flight recording? — &lt;a href="https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/about.htm#JFRUH170" rel="noopener noreferrer"&gt;JFR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we profile our application using JFR, it’ll show different thread events. Let’s try to map these thread events to the states we discussed above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0q0mwcno2z0s13iu8ae2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0q0mwcno2z0s13iu8ae2.png" alt="Thread profiling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Java Thread Parked
&lt;/h4&gt;

&lt;p&gt;This event occurs when a thread calls the LockSupport.park() method or its variants, which causes the thread to suspend its execution until it is unparked by another thread or a timeout expires. This is often used by concurrency utilities, such as java.util.concurrent.locks, to implement efficient blocking mechanisms. This event corresponds to the &lt;strong&gt;Waiting&lt;/strong&gt; or &lt;strong&gt;Timed Waiting&lt;/strong&gt; state, depending on whether the park() method was called with a timeout parameter or not.&lt;/p&gt;

&lt;h4&gt;
  
  
  Java Thread Sleep
&lt;/h4&gt;

&lt;p&gt;This event occurs when a thread calls the Thread.sleep() method or its variants, which causes the thread to suspend its execution for a specified amount of time. This is often used to introduce delays or pauses in the execution flow. This event corresponds to the &lt;strong&gt;Timed Waiting&lt;/strong&gt; state.&lt;/p&gt;

&lt;h4&gt;
  
  
  Java Monitor Wait
&lt;/h4&gt;

&lt;p&gt;This event occurs when a thread calls the Object.wait() method or its variants, which causes the thread to release the monitor (lock) of an object and wait until it is notified by another thread or a timeout expires. This is often used to implement inter-thread communication and coordination. This event corresponds to the &lt;strong&gt;Waiting&lt;/strong&gt; or &lt;strong&gt;Timed Waiting&lt;/strong&gt; state, depending on whether the wait() method was called with a timeout parameter or not.&lt;/p&gt;

&lt;h4&gt;
  
  
  Java Monitor Blocked
&lt;/h4&gt;

&lt;p&gt;This event occurs when a thread tries to acquire the monitor lock (inside a synchronized method/block) of an object that is already owned by another thread, which causes the thread to block until the monitor is released. This is often used to implement mutual exclusion and synchronization. This event corresponds to the &lt;strong&gt;Blocked&lt;/strong&gt; state.&lt;/p&gt;

&lt;h4&gt;
  
  
  Socket Read
&lt;/h4&gt;

&lt;p&gt;This event occurs when a thread calls the SocketInputStream.read() method or its variants, which causes the thread to read data from a socket. This is often used to implement network communication. This event corresponds to the &lt;strong&gt;Blocked&lt;/strong&gt; state.&lt;/p&gt;

&lt;h4&gt;
  
  
  Socket Write
&lt;/h4&gt;

&lt;p&gt;This event occurs when a thread calls the SocketOutputStream.write() method or its variants, which causes the thread to write data to a socket. This is also often used to implement network communication. This event corresponds to the &lt;strong&gt;Blocked&lt;/strong&gt; state.&lt;/p&gt;

&lt;h4&gt;
  
  
  File Read
&lt;/h4&gt;

&lt;p&gt;This event means that the thread is reading data from a file by using the read() method of the File class or its subclasses. This method can block the thread until some data is available or an exception occurs. This event corresponds to the &lt;strong&gt;Blocked&lt;/strong&gt; state.&lt;/p&gt;

&lt;h4&gt;
  
  
  File Write
&lt;/h4&gt;

&lt;p&gt;This event means that the thread is writing data to a file by using the write() method of the File class or its subclasses. This method can block the thread until the data is written or an exception occurs. This event corresponds to the &lt;strong&gt;Blocked&lt;/strong&gt; state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Profiling examples:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Example 1
&lt;/h4&gt;

&lt;p&gt;Let’s assume we have a SpringBoot (embedded tomcat, default thread pool size = 200) service that exposes a GET /portfolio/{userId} endpoint to download a users portfolio. The controller forwards the request to the PortfolioDownloader class. This class allows only 5 threads to execute the critical section in parallel at any point in time using a Semaphore (i.e only 5 concurrent download requests). If we fire 200 concurrent download requests, what would be the state of the rest of the threads? Let’s profile and find out:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public enum PortfolioDownloader {

  INSTANCE;

  private static final Semaphore lock = new Semaphore(5);

  public String downloadPortfolio(String userId) {
    try {
      lock.acquire();
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    System.out.println("Thread " + Thread.currentThread() + " downloading portfolio for user " + userId);
    //Mock download
    long aMockVariable = 0L;
    long downloadStartTime = System.currentTimeMillis();
    while (System.currentTimeMillis() - downloadStartTime &amp;lt; 2000) {
      aMockVariable++;
    }

    lock.release();
    return "s3://portfolioapp-" + aMockVariable + ".portfolio-" + userId;
  }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskau6yj1tthus8psk454.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskau6yj1tthus8psk454.png" alt="Example 1 state changes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95nuo0e1hcil27ashgzw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95nuo0e1hcil27ashgzw.png" alt="Example 1 logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;i.e Thread 5, 4, 7, 1, and 2 acquire the permit first and go about their task while Thread 23 (at the end) gets the permit after some time. This will also reflect in the Thread profiling page:&lt;/p&gt;

&lt;p&gt;See how Thread 23 was parked for some time until it got the permit&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — Threads call the Semaphore.accquire() method to get a permit. If a permit is not available, the method internally uses the LockSupport.park() to suspend threads until a permit is available!&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 2
&lt;/h4&gt;

&lt;p&gt;Let’s assume we have the following SpringBoot application:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@RestController
@RequestMapping("/v1/counter")
public class CounterResource {

  @PostMapping
  public ResponseEntity&amp;lt;Integer&amp;gt; increment() {
    return ResponseEntity.ok(Counter.increment());
  }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public final class Counter {

  private static int COUNT = 0;

  public synchronized static int increment() {
    try {
      Thread.sleep(50);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    ++COUNT;
    System.out.println("Thread " + Thread.currentThread() + " called. Current count = " + COUNT);
    return COUNT;
  }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We’ll fire 2087 concurrent requests to this API&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjaglm4gztn44ildjxoa4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjaglm4gztn44ildjxoa4.png" alt="Example 2 logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thread 14 acquires the monitor on the object first. See how Thread 68 goes into the Blocked State (Java Monitor Blocked) before it is able to acquire the monitor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmq1pbo73omnsqmvnw072.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmq1pbo73omnsqmvnw072.png" alt="Example 2 profiling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try to do thread profiling of a simple consumer producer (using wait() and notify()) application yourself using JFR and note the thread states!&lt;/p&gt;

</description>
      <category>java</category>
      <category>multithreading</category>
      <category>profiling</category>
    </item>
    <item>
      <title>Introduction to unit testing in Java</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Thu, 28 Dec 2023 14:52:29 +0000</pubDate>
      <link>https://dev.to/ishansoni22/introduction-to-unit-testing-in-java-g0j</link>
      <guid>https://dev.to/ishansoni22/introduction-to-unit-testing-in-java-g0j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The point of writing tests is not only to ensure that your code works now, but to ensure that the code will continue to work in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What does a typical test run involve?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Prepration&lt;/li&gt;
&lt;li&gt;Provide test input&lt;/li&gt;
&lt;li&gt;Running the test case&lt;/li&gt;
&lt;li&gt;Provide expected output&lt;/li&gt;
&lt;li&gt;Verify output (expected vs actual)&lt;/li&gt;
&lt;li&gt;Do something if the test fails (expected and output do not match)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;3, 5, 6 are common tasks while 1, 2, 4 change based on your test. Instead of doing everything yourself, offload the common tasks to a framework — JUnit is the defacto unit testing framework in Java. The current JUnit framework release is &lt;strong&gt;5&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problems with JUnit4
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Outdated. &amp;gt; 10 years old&lt;/li&gt;
&lt;li&gt;No support for newer testing patterns&lt;/li&gt;
&lt;li&gt;Was playing catch up with newer java features&lt;/li&gt;
&lt;li&gt;Had a monolothic architecture (one jar that had it all)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  JUnit5 Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft13x1xbw3te9mmd4dnyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft13x1xbw3te9mmd4dnyv.png" alt="JUnit5 Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;Let’s test the deposit and withdraw functionality of the Account aggregate:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Transaction {

    public enum Type {
        CREDIT, DEBIT, HOLD
    };

    public Transaction(String accountId, Type type, BigDecimal amount, String reference) {
        this.id = UUID.randomUUID().toString();
        this.transactionTime = System.currentTimeMillis();
        this.accountId = accountId;
        this.type = type;
        this.amount = amount;
        this.reference = reference;
    }

    private final String id;

    private final String accountId;

    private final long transactionTime;

    private final Type type;

    private final BigDecimal amount;

    private final String reference;

    //Getters

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Account {

    private String id;

    private String userId;

    private BigDecimal balance = BigDecimal.ZERO;

    Transaction deposit(BigDecimal amount, String reference) {
        balance = balance.add(amount);
        return new Transaction(id, Transaction.Type.CREDIT, amount, reference);
    }

    Transaction withdraw(BigDecimal amount, String reference) {
        balance = balance.subtract(amount);
        return new Transaction(id, Transaction.Type.DEBIT, amount, reference);
    }

    //Getters and Setters

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add the jupiter-engine and jupiter-api dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependencyManagement&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.junit&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;junit-bom&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;5.8.2&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
            &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;  
    &amp;lt;/dependencies&amp;gt;
&amp;lt;/dependencyManagement&amp;gt;

&amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;junit-jupiter-api&amp;lt;/artifactId&amp;gt;
        &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;junit-jupiter-engine&amp;lt;/artifactId&amp;gt;
        &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a test class in the src/java/test directory using the same package convention (so that you get access to all default properties of the class you want to test) and annotate your methods with &lt;a class="mentioned-user" href="https://dev.to/test"&gt;@test&lt;/a&gt; (from the jupiter jar!) or use your IDE to create a test:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AccountTest {

    @Test
    public void testDeposit() {
        Account account = new Account();
        Transaction transaction1 = account.deposit(BigDecimal.TEN, "FY 23-24 qtr 2 interest");
        Transaction transaction2 = account.deposit(BigDecimal.ONE, "Transaction XYZ Surcharge waiver");

        assertEquals(BigDecimal.valueOf(11), account.getBalance());
        assertEquals(Transaction.Type.CREDIT, transaction1.getType());
        assertEquals(BigDecimal.TEN, transaction1.getAmount());

    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running the test from the IDE:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijovrtmjerfdzyx51xeb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijovrtmjerfdzyx51xeb.png" alt="Assert Passed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Assertions (Leverage JUnit’s way of reporting an error!)
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import static org.junit.jupiter.api.Assertions.*; //static import - contains all the assert methods!

assertEquals(expected, actual) //Example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Using assertions you can assert/verify if your code is producing the desired results or not. Your test will fail if these assertions fail. Example, let’s change our deposit method and introduce a bug:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction deposit(BigDecimal amount, String reference) {
    //We are not using the return value of the add method. 
    //Note: BigDecimal is an immutable object! 
    balance.add(amount);
    return new Transaction(id, Transaction.Type.CREDIT, amount, reference);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If we run our test case now, it will fail:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9u1231f4rxozzxoqz1tp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9u1231f4rxozzxoqz1tp.png" alt="Assert failed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Be a little more descriptive when your test fails:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertEquals(BigDecimal.valueOf(11), account.getBalance(), "The account balance after two deposits of 10 and 1 should be 11");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsofdfoygs7tgvgqj94tp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsofdfoygs7tgvgqj94tp.png" alt="Assert descriptive"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Asserting Exceptions using assertThrows&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MathUtils {

    //Other methods omitted

    public int divide(int a, int b) {
        return a / b;
  }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MathUtilsTest {

    @Test
    void testDivideBy0() {
        MathUtils mathUtils = new MathUtils();
        /*
        assertThrows(Exception class, Executable)
        public interface Executable {
            void execute() throws Throwable;
        }
       */
       assertThrows(ArithmeticException.class, () -&amp;gt; mathUtils.divide(1, 0),
    "Divide by 0 should raise an arithmetic exception");
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Running tests using maven instead of your IDE
&lt;/h4&gt;

&lt;p&gt;If you try to run mvn test, the tests are not executed!&lt;/p&gt;

&lt;p&gt;Use the maven surefire plugin. This will allow you to run tests using the mvn test command (The plugin binds a goal to execute all test when the test phase is executed). You can then potentially automate test execution in your CI/CD pipeline!&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
        &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;maven-surefire-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;3.0.0-M7&amp;lt;/version&amp;gt;
        &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Test Life Cycle and Hooks (BeforeAll, BeforeEach, AfterEach, AfterAll)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4e6c7i8cx8e4s2ay3jn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4e6c7i8cx8e4s2ay3jn.png" alt="Test life cycle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv9q8pn6aaio2z7h47px.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv9q8pn6aaio2z7h47px.png" alt="Hooks"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AccountTest

    private Account account;

    @BeforeAll
    public static void beforeAll() {
        System.out.println("In before all");
    }

    @BeforeEach
    public void beforeEach() {
        System.out.println("In before each");
        account = new Account();
    }

    @AfterEach
    public void afterEach() {
        System.out.println("In after each");
    }

    @AfterAll
    public static void afterAll() {
        System.out.println("In after all");
    }

    @Test
    public void testDeposit() {
        Transaction transaction1 = account.deposit(BigDecimal.TEN, "FY 23-24 qtr 2 interest");
        Transaction transaction2 = account.deposit(BigDecimal.ONE, "Transaction XYZ Surcharge waiver");

        assertEquals(BigDecimal.valueOf(11), account.getBalance(), "The account balance after two deposits of 10 and 1 should be 11");
        assertEquals(Transaction.Type.CREDIT, transaction1.getType());
        assertEquals(BigDecimal.TEN, transaction1.getAmount());
    }

    @Test
    public void testWithdraw() {
        Transaction transaction = account.withdraw(BigDecimal.TEN, "ATM withdrawal");

        assertEquals(BigDecimal.valueOf(-10), account.getBalance(), "The account balance after a withdrawal of 10 should be -10");
        assertEquals(Transaction.Type.DEBIT, transaction.getType());
        assertEquals(BigDecimal.TEN, transaction.getAmount());
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbtdidpv3k8xr94j7h77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbtdidpv3k8xr94j7h77.png" alt="Hooks result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; — The @BeforeAll and @AfterAll are called before the instance of the test class is created/after the instance of the test class is destroyed. Therefore, they should be static methods!&lt;/p&gt;

&lt;h3&gt;
  
  
  Paramaterised Tests
&lt;/h3&gt;

&lt;p&gt;Allows you to execute a single test method multiple times with different parameters&lt;/p&gt;

&lt;p&gt;Also allows you to specify the source of your tests&lt;/p&gt;

&lt;p&gt;In order to use parameterised test, add the &lt;strong&gt;junit-jupiter-params&lt;/strong&gt; artifact&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;junit-jupiter-params&amp;lt;/artifactId&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then annotate your test methods with &lt;strong&gt;@ParameterizedTest&lt;/strong&gt; + Specify a Source. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MathUtils {

  public double areaOfCircle(double radius) {
    return Math.PI * radius * radius;
  }

  public int add(int a, int b) {
    return a + b;
  }

  public int divide(int a, int b) {
    return a / b;
  }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ParameterizedTest
@ValueSource(doubles = {1d, 2d, 3d, 4d, 5d})
void testCircleArea(double radius) {
  MathUtils math = new MathUtils();
  double expected = Math.PI * radius * radius;
  double actual = math.areaOfCircle(radius);

  assertEquals(expected, actual, "Area of circle is PI * r * r");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpy0md83ed50iscqz4sty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpy0md83ed50iscqz4sty.png" alt="Value source"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the single test method was run 5 times with values 1, 2, 3, 4, and 5 passed to the double radius parameter respectively.&lt;/p&gt;

&lt;p&gt;With the &lt;strong&gt;@ValueSource&lt;/strong&gt; you can pass a single literal value (ints, strings, floats, doubles, etc. to your test case)&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;@NullSource&lt;/strong&gt;, &lt;strong&gt;@EmptySource&lt;/strong&gt;, &lt;strong&gt;@NullAndEmpty&lt;/strong&gt; source to pass null/empty sources&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;@EnumSource&lt;/strong&gt; to pass enums&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;@CsvSource&lt;/strong&gt; to pass in multiple literal arguments&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ParameterizedTest
@CsvSource({"1, 1, 2", "2, 2, 4", "5, 5, 10", "15, 15, 30", "50, 50, 100"})
void testAdd(int a, int b, int expected) {
  MathUtils math = new MathUtils();
  int actual = math.add(a, b);
  assertEquals(expected, actual, "adding " + a + ", " + b + " should result in " + expected);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwukvha4k10po8yrdztak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwukvha4k10po8yrdztak.png" alt="CSV source"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;@MethodSource&lt;/strong&gt; to provide more complex arguments&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ParameterizedTest
@MethodSource("divideSource")
void testDivideBy0(int a, int b) {
  /*
  assertThrows(Exception class, Executable)
  public interface Executable {
     void execute() throws Throwable;
  }
   */
  assertThrows(ArithmeticException.class, () -&amp;gt; this.math.divide(a, b),
      "Divide by 0 should raise an arithmetic exception");
}

//Should be static and return a stream/collection of Arguments
//Each Arguments item will initiate a test execution using said arguments!
//Can be in a separate class as well. You'll then need to use fully qualified name in the @MethodSource annotation
//Arguments.of(...Object) - Can pass complex arguments
static Stream&amp;lt;Arguments&amp;gt; divideSource() {
  return Stream.of(
      Arguments.of(1, 0),
      Arguments.of(2, 0),
      Arguments.of(3, 0)
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhp55pvqlpzwhu44fs6r4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhp55pvqlpzwhu44fs6r4.png" alt="Method source"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mockito
&lt;/h3&gt;

&lt;p&gt;Mockito is a Java-based mocking framework that is used for unit testing of Java applications. It allows you to create mock objects that simulate the behavior of real objects, and verify their interactions and expectations. Mockito can be used with other testing frameworks, such as JUnit and is useful for testing components that depend on other components, but are not yet available or are difficult to access. By using mock objects, you can isolate the component under test and focus on its logic and functionality.&lt;/p&gt;

&lt;p&gt;Add the &lt;strong&gt;mockito-core&lt;/strong&gt; and the &lt;strong&gt;mockito-junit-jupiter&lt;/strong&gt; (A 3rd party extension library for junit and mockito integration) dependency&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- https://mvnrepository.com/artifact/org.mockito/mockito-core --&amp;gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;org.mockito&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;mockito-core&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;4.9.0&amp;lt;/version&amp;gt;
  &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter --&amp;gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;org.mockito&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;mockito-junit-jupiter&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;4.9.0&amp;lt;/version&amp;gt;
  &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s extend our Accounts functionality and add a service. This example assumes you have a SpringBoot project (with Spring Data). The Account and Transaction Repository are Spring Data repositories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class AccountApplicationService {

    @Autowired
    private AccountRepository accountRepository;

    @Autowired
    private TransactionRepository transactionRepository;

    @Transactional
    public void deposit(String accountId, BigDecimal amount, String reference) {
        Account account = accountRepository.getAccountById(accountId).orElseThrow();
        Transaction transaction = account.deposit(amount, reference);
        transactionRepository.save(transaction);
        accountRepository.save(account);
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s try to test AccountApplicationService. To test it, I want to Mock the AccountRepository and TransactionRepository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ExtendWith(MockitoExtension.class)
class AccountApplicationServiceTest {

    private Account account;

    //Create a mock for this dependency
    @Mock
    private AccountRepository accountRepository;

    //Create a mock for this dependency
    @Mock
    private TransactionRepository transactionRepository;

    //@InjectMocks creates an instance of this class and injects the mocks that are created with the @Mock (or @Spy) annotations into this instance
    @InjectMocks
    private AccountApplicationService accountApplicationService;

    @BeforeEach
    public void beforeEach() {
        this.account = new Account();
        //Whenever the accountRepository.getAccountById method is called,
        //return the given account object
        Mockito.when(accountRepository.getAccountById(Mockito.anyString())).thenReturn(Optional.of(account));
    }

    @Test
    public void testDeposit() {
        accountApplicationService.deposit("temp", BigDecimal.TEN, "FY 23-24 qtr 2 interest");
        assertEquals(BigDecimal.TEN, account.getBalance());
        //Verify if the deposit call resulted in a call to the save method of the transactionRepository
        Mockito.verify(transactionRepository).save(Mockito.any());
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>java</category>
      <category>unittest</category>
      <category>junit5</category>
      <category>mockito</category>
    </item>
    <item>
      <title>Saga, CDC with Transactional Inbox/Outbox</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Fri, 22 Dec 2023 11:22:07 +0000</pubDate>
      <link>https://dev.to/ishansoni22/saga-cdc-with-transactional-inboxoutbox-3of7</link>
      <guid>https://dev.to/ishansoni22/saga-cdc-with-transactional-inboxoutbox-3of7</guid>
      <description>&lt;p&gt;In a distributed system, some business transactions may span multiple services. How to implement transactions that span multiple services? — &lt;a href="https://microservices.io/patterns/data/saga.html" rel="noopener noreferrer"&gt;The Saga Pattern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And not just a business transaction, what are some other strategies to keep a distributed system eventually consistent? i.e. when there are multiple systems that store related data across them, what are some common strategies to keeping them consistent? — another such strategy is &lt;a href="https://medium.com/@bijit211987/change-data-capture-with-microservices-79fa90aaf0b3" rel="noopener noreferrer"&gt;CDC (Change Data Capture)&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Don’t confuse Saga and CDC
&lt;/h3&gt;

&lt;p&gt;Saga is a substitute for a distributed transaction. An example would be: Let’s assume that our business rule (invariant) says that when a user places an order, it will only be fulfilled if the order total is within the user’s credit limit or balance (and deduct the order total from the credit limit). If the credit limit check/deduction fails, the order will not be fulfilled. In this case, the transaction spans over the order service (which manages the order) and the customer service (which manages the customer credit). You can use Saga in this case.&lt;/p&gt;

&lt;p&gt;An example for CDC would be: Let’s assume you have a customer service that manages customer data (user-handle, email, name, etc.) and a notification service that sends out emails. The notification service caches some user data so that it does not have to query the customer service every time it has to send out an email to the customer. Now, what if the customer changes their name? That change has to be propagated to the notification service as well. Note that a transaction in this case finishes as soon as the change is committed in the customer service. But since some customer-related data is stored by the notification service as well, we need to make it consistent. You can use CDC in this case.&lt;/p&gt;

&lt;h3&gt;
  
  
  But there are still some problems we need to tackle in both Saga and CDC:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Guaranteed event/message delivery
&lt;/h4&gt;

&lt;p&gt;Suppose you have a microservice (A) that needs to update it’s own database and publish an event to another microservice (B, using a message broker) as part of a saga step. It will probably do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Update the concerned aggregate/entity in a transaction&lt;/li&gt;
&lt;li&gt;Create and send an event to the message broker&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, what if the event publishing to the messaging broker failed (let’s say because of a network issue?)&lt;/p&gt;

&lt;h4&gt;
  
  
  Retry capabilities
&lt;/h4&gt;

&lt;p&gt;Let’s say you are using CDC. Sometimes, you may want retry capabilities in your application. What if microservice B is not able to consume an event due to a data/code issue? You spot it and quickly do a hotfix. But, you may also want to retry the events that were sent by microservice A which microservice B was not able to consume before the hotfix!&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the Transactional Inbox/Outbox Pattern along with Saga/CDC
&lt;/h3&gt;

&lt;p&gt;Transactional Inbox/Outbox pattern is a technique used in microservices architecture to ensure reliable and consistent data exchange between different services. It works by using a database table as an intermediary between the service that produces the data and the service that consumes it. The producer service inserts the data into the outbox table as part of the original business transaction (which ensures atomicity of the database update and the event insertion), and then a separate process or component reads the data from the outbox table and publishes it to a message broker or another service. The consumer service can then receive the data from the message broker, and store it in its own inbox table. This way, the data is always available for the consumer service, even if the producer service or the message broker is down or unreachable. The Transactional Inbox/Outbox pattern can help with some common challenges in microservices communication, such as atomicity, durability, scalability, and performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxthdb2re2pr52mog8add.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxthdb2re2pr52mog8add.png" alt="Transactional Outbox. Original credits — https://medium.com/design-microservices-architecture-with-patterns/outbox-pattern-for-microservices-architectures-1b8648dfaa27"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1bqhtzpgcmav5opsr9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1bqhtzpgcmav5opsr9i.png" alt="Transactional Inbox. Original credits — https://medium.com/event-driven-utopia/sending-reliable-event-notifications-with-transactional-outbox-pattern-7a7c69158d1b"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;i.e. whenever you want to publish an event, you first put it into an outbox table (in the same transaction as whatever generated the event in the first place). Some event emitter polls those and sends them, giving you at-least once delivery (solves the guaranteed delivery problem). If the business trasaction generating the event gets rolled back, the event dosen’t get saved; if the emitter fails to publish, the event still sits in the DB. You can add a status to each event in the DB (PENDING, PUBLISHED). The poller will pick up PENDING events and mark them as PUBLISHED after publishing them to a message broker. Setting the status of an event back to PENDING will allow you to resend the message if needed (solves the retry capability problem). You may be tempted to skip the inbox table but it may come in handy to create &lt;strong&gt;&lt;a href="https://microservices.io/patterns/communication-style/idempotent-consumer.html" rel="noopener noreferrer"&gt;idempotent consumers&lt;/a&gt;&lt;/strong&gt;. In case you want to retry an event, your consumers should have the capability to discard duplicate events. If your domain model doesn’t have this capability, the inbox table will be useful here — If the event-id is already present in the inbox table, you can skip this event!&lt;/p&gt;

</description>
      <category>saga</category>
      <category>cdc</category>
      <category>microservices</category>
      <category>transactionaloutbox</category>
    </item>
    <item>
      <title>Views, Materialized Views, and Spring Data JPA</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Thu, 21 Dec 2023 08:20:11 +0000</pubDate>
      <link>https://dev.to/ishansoni22/views-materialized-views-and-spring-data-jpa-3lbn</link>
      <guid>https://dev.to/ishansoni22/views-materialized-views-and-spring-data-jpa-3lbn</guid>
      <description>&lt;h3&gt;
  
  
  Views
&lt;/h3&gt;

&lt;p&gt;A view in an RDBMS is a &lt;strong&gt;virtual table&lt;/strong&gt; that is defined by a query. It can combine data from two or more tables, or just contain a subset of information from a single table. A view does not store data, but rather displays the data that is stored in the base tables. You can query a view like you can a table, but you cannot insert, update, or delete data from a view unless certain conditions (not discussed here) are met!&lt;/p&gt;

&lt;p&gt;i.e store complex queries under a name (&lt;strong&gt;it’s almost like a variable for a query&lt;/strong&gt;)!&lt;/p&gt;

&lt;p&gt;Let’s assume we have the following tables: (We are using &lt;strong&gt;Postgres&lt;/strong&gt; in this example)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Department table&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98rpnt317xr5m7opurn4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98rpnt317xr5m7opurn4.png" alt="Department table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Employee table&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00cv3ljernmw7mrr5wle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00cv3ljernmw7mrr5wle.png" alt="Employee table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Employee-Department table&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ou6gchknr2vk86q6kf8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ou6gchknr2vk86q6kf8.png" alt="Employee-Department table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, individually these tables do not mean much. I’ll have to run the following query to get some meaningful data:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select e.id, e.name, e.email, d.name, ed.start_time,  ed.end_time
from employee e join employee_department ed on e.id = ed.employee_id
join department d on d.id = ed.department_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy05gzo41kbrsp9n569h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy05gzo41kbrsp9n569h.png" alt="Join"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s say you want to group by department name and count the number of employees:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select d.name, count(e.id) 
from employee e join employee_department ed on e.id = ed.employee_id
join department d on d.id = ed.department_id
group by d.name;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49ucxs8aibqmma28fu41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49ucxs8aibqmma28fu41.png" alt="Group By"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This join query “&lt;strong&gt;from employee e join employee_department ed on e.id = ed.employee_id join department d on d.id = ed.department_id&lt;/strong&gt;” is duplicated every time you want to work with employees and departments!&lt;/p&gt;

&lt;p&gt;I can instead, create a view off of this query and give it a name and treat it as if it were a table!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- create view &amp;lt;view-name&amp;gt; AS &amp;lt;your query&amp;gt;

create view employee_with_department_details AS

select e.id as employee_id, e.name as employee_name, e.email, d.name as department_name, ed.start_time, ed.end_time
from employee e join employee_department ed on e.id = ed.employee_id
join department d on d.id = ed.department_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, I can directly query this view as if it were a table&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select * from employee_with_department_details;

select department_name, count(employee_id)
from employee_with_department_details
group by department_name;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Important — Whenever you execute a view, the query behind the view is executed every-time!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Query views directly in a Spring Data Jpa application using Interface Projections
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface EmployeeWithDepartmentDetails {
    @Value("#{target.employee_id}")
    Long getEmployeeId();
    @Value("#{target.employee_name}")
    String getEmployeeName();
    String getEmail();
    @Value("#{target.department_name}")
    String getDepartmentName();
    @Value("#{target.start_time}")
    Long getStartTime();
    @Value("#{target.end_time}")
    Long getEndTime();
}

@Repository
public interface DepartmentJpaRepository extends JpaRepository&amp;lt;Department, Long&amp;gt; {

    @Query(
            value = "select * from employee_with_department_details;",
            nativeQuery = true
    )
    List&amp;lt;EmployeeWithDepartmentDetails&amp;gt; getEmployeeWithDepartmentDetails();

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Query the view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozji511ernl3cqqz7xt8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozji511ernl3cqqz7xt8.png" alt="View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Materialized Views
&lt;/h3&gt;

&lt;p&gt;The main difference between a view and a materialized view in an RDBMS is that a view does not store any data, while a materialized view does. A view is a virtual table that is defined by a query, and it displays the data that is stored in the base tables. A materialized view is a view that stores the result set of the query in a physical table, and it can be queried like a regular table. However, a materialized view needs to be refreshed periodically to reflect the changes in the base tables, while a view is always up to date.&lt;/p&gt;

&lt;p&gt;Therefore, a materialised view does 2 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Store the query that is used to create this materialised view (also done by a view)&lt;/li&gt;
&lt;li&gt;Store the data returned by this query!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unlike a normal view, a materialised view does not execute the query every time you fetch it, but rather returns the data stored by the view thus &lt;strong&gt;improving performance&lt;/strong&gt;. That is why the data in a materialised view can become stale and require a refresh.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- create materialized view &amp;lt;view-name&amp;gt; AS &amp;lt;your query&amp;gt;

create materialized view employee_with_department_details  AS

select e.id as employee_id, e.name as employee_name, e.email, d.name as department_name, ed.start_time, ed.end_time
from employee e join employee_department ed on e.id = ed.employee_id
join department d on d.id = ed.department_id;

select * from employee_with_department_details;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To refresh a materialised view:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;refresh materialized view employee_with_department_details;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Query and update materialized views in a Spring Data Jpa application
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Example use case&lt;/strong&gt;: We want to refresh the materialized view whenever a new employee is added to a department so that the data returned by the materialized view is always current&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Transactional
public AddEmployeeToDepartment addEmployeeToDepartment(Long employeeId, Long departmentId) {
    final Department department = departmentJpaRepository.findById(departmentId).orElseThrow();
    final Employee employee = employeeJpaRepository.findById(employeeId).orElseThrow();
    final EmployeeDepartment employeeDepartment = new EmployeeDepartment(employeeId, departmentId);
    employeeDepartmentJpaRepository.save(
        employeeDepartment
    );

    //Refresh the materialised view since it will contain stale data otherwise!
    employeeDepartmentJpaRepository
        .refreshEmployeeWithDepartmentDetails();

    return new AddEmployeeToDepartment(true, null);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Repository
public interface EmployeeDepartmentJpaRepository extends JpaRepository&amp;lt;EmployeeDepartment, Long&amp;gt; {

    @Query(
            value = "select * from employee_with_department_details;",
            nativeQuery = true
    )
    List&amp;lt;EmployeeWithDepartmentDetails&amp;gt; getEmployeeWithDepartmentDetails();

    /*
    The refresh materialized view statement does not return any result set,
    but Spring Data JPA expects one. To fix this, you need to annotate your method with @Modifying,
    which tells Spring Data JPA that the query is a DML statement and does not return any result.
    You can also use the clearAutomatically attribute to clear the persistence context after the query execution,
    which might be useful if you want to query the refreshed view afterwards.
    */
    @Modifying(clearAutomatically = true)
    @Transactional
    //use @Async instead!
    @Query(
            value = "refresh materialized view employee_with_department_details;",
            nativeQuery = true
    )
    void refreshEmployeeWithDepartmentDetails();

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Query the materialised view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ueb925z76hnulql40mo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ueb925z76hnulql40mo.png" alt="Materialized View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s try to add a new employee (Mr Robot) and tag them to the IT department and see if the materialized view is refreshed or not:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsedtustti23m1gw77yvr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsedtustti23m1gw77yvr.png" alt="There you go"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ideally, you should not use materialized views for data that is updated very frequently. The data is basically static until you refresh it, at which time it’s flushed and the data is replaced by the result of the query at the new run time. They’re particulary good when the performance to run the query is poor but the data doesn’t have to be exact or up to the last second. For example, if you wanted to run a query that generates a report for the previous day, you could create a materialized view to get the data from yesterday and run it on a schedule after midnight. Then the user can query the materialized view with a select * in the morning and get quick results without waiting on the query to execute against the base data. Sometimes, it makes sense to have the materialized view to contain most of the result set and then some optimized query to just pull data from the current day, hour, etc. and union the results together.&lt;/p&gt;

</description>
      <category>rdbms</category>
      <category>materializedviews</category>
      <category>java</category>
      <category>springdatajpa</category>
    </item>
    <item>
      <title>Using Spring Application Events within Transactional Contexts</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Wed, 20 Dec 2023 13:05:31 +0000</pubDate>
      <link>https://dev.to/ishansoni22/using-spring-application-events-within-transactional-contexts-3p68</link>
      <guid>https://dev.to/ishansoni22/using-spring-application-events-within-transactional-contexts-3p68</guid>
      <description>&lt;p&gt;This post is inspired by &lt;a href="https://youtu.be/h8TWQM6fKNQ?si=zv7-S_MFMDAgJU9L" rel="noopener noreferrer"&gt;this talk&lt;/a&gt; given by Bartłomiej Słota. I highly recommend checking it out.&lt;/p&gt;

&lt;p&gt;Application events are a great way of communicating between beans defined in the same application or bounded context.&lt;/p&gt;

&lt;p&gt;It is a great way of implementing the &lt;strong&gt;&lt;a href="https://dev.to/ishansoni22/the-observer-design-pattern-51bi"&gt;Observer Design Pattern&lt;/a&gt;&lt;/strong&gt;. "&lt;em&gt;Hey, a customer is created. If anybody is interested, here is the event. Do whatever you want to do with it!&lt;/em&gt;"&lt;/p&gt;

&lt;p&gt;In Spring, you can publish events using the &lt;strong&gt;ApplicationEventPublisher&lt;/strong&gt;, which is the super interface for ApplicationContext and is the abstraction around the Spring event bus. In the latest versions of Spring, you can simply use POJOs as events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: Create a customer and then generate a verification token that will be sent to their email ID using which they can verify their account. These two should be independent processes, i.e., creating a customer and generating a verfication token should happen in two separate transactions!&lt;/p&gt;

&lt;p&gt;The Customer entity and the CustomerJpaRepository&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;
    private String verificationToken;
    private boolean verified;

    public Customer(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public void setVerificationToken(String generatedToken) {
        this.verificationToken = generatedToken;
    }

    public boolean verifyCustomer(String providedToken) {
        if (StringUtils.hasLength(this.verificationToken) &amp;amp;&amp;amp; this.verificationToken.equals(providedToken)) {
            this.verified = true;
        }
        return this.verified;
    }
    //Getters and Setters
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Repository
public interface CustomerJpaRepository extends JpaRepository&amp;lt;Customer, Long&amp;gt; {
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The CustomerService. The createCustomer method will create a customer and emit a CustomerCreated event using Spring’s ApplicationEventPublisher&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class CustomerService {

    private final CustomerJpaRepository customerJpaRepository;

    private final ApplicationEventPublisher applicationEventPublisher;

    public CustomerService(CustomerJpaRepository customerJpaRepository, ApplicationEventPublisher applicationEventPublisher) {
        this.customerJpaRepository = customerJpaRepository;
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Transactional
    public Customer createCustomer(String name, String email) {
        final Customer customer = customerJpaRepository.save(new Customer(name, email));
        applicationEventPublisher.publishEvent(new CustomerCreated(customer.getId()));
        return customer;
   }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To create subscribers, you can use Spring’s &lt;strong&gt;@EventListener&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component
public class VerificationTokenGenerator {

    private final CustomerJpaRepository customerJpaRepository;

    public VerificationTokenGenerator(CustomerJpaRepository customerJpaRepository) {
        this.customerJpaRepository = customerJpaRepository;
    }

    @EventListener
    public void generateVerificationToken(CustomerCreated customerCreated) {
        final Long customerId = customerCreated.getId();
        final Customer customer = customerJpaRepository.findById(customerId).get();
        customer.setVerificationToken(
            String.valueOf(
                Objects.hashCode(customer)
            )
        ); //Dummy

        customerJpaRepository.save(customer);
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Is there a separation of concerns here? i.e does the token generation process happen in a separate transaction? — &lt;strong&gt;No&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Why? — By default, Spring events are synchronous i.e the producer won’t proceed unless all listeners have been executed i.e if the producer is executing inside a transaction, the transaction is propagated to all listeners i.e in this case the generateVerificationToken() is executed within the same transaction as createCustomer()&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ftr0ymv9zxi5qs2eesi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ftr0ymv9zxi5qs2eesi.png" alt="Event Listener"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See above — The createCustomer() has executed, and we are currently in the generateVerificationToken(), still a “select * from customer” query returns no result! Once the generateVerificationToken method is successful, the transaction is committed!&lt;/p&gt;

&lt;p&gt;But, how does customerJpaRepository.findById() in the generateVerificationToken() returns back a customer if the transaction is not committed? — Hibernate’s First Level Cache!&lt;/p&gt;

&lt;p&gt;But I want the generateVerificationToken() to be executed in a separate transaction. How do we get around this? — use &lt;strong&gt;@TransactionalEventListener&lt;/strong&gt;. i.e using @TransactionalEventListener, you can collaborate with surrounding transactions using Phases!&lt;/p&gt;

&lt;p&gt;By default the phase is AFTER_COMMIT, i.e execute this listener only after the enclosing transaction has committed&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F86uo34u8vgf1dipzp4g0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F86uo34u8vgf1dipzp4g0.png" alt="Phases"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we change our code to this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@TransactionalEventListener
public void generateVerificationToken(CustomerCreated customerCreated) {
    final Long customerId = customerCreated.getId();
    final Customer customer =   customerJpaRepository.findById(customerId).get();
    customer.setVerificationToken(
        String.valueOf(
            Objects.hashCode(customer)
        )
    ); //Dummy

    customerJpaRepository.save(customer);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmj09hfjst21hgdjkkaoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmj09hfjst21hgdjkkaoy.png" alt="Transactional Event Listener"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The event listener receives the event only after the transaction has committed. But the verification token is not saved. It’s like the second save didn’t work at all!&lt;/p&gt;

&lt;p&gt;Think about it, the listener is executed only after the original transaction commits. A transaction is all or nothing. Once the transaction has committed, you cannot do anything more!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgcjwyzzf7cn9vdsla2cm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgcjwyzzf7cn9vdsla2cm.png" alt="After Commit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use this to do something after the transaction has successfully committed. Things like sending an email or to publish a message to a messaging broker, but if you want to do additional DB work, it won’t work! Instead you need to start another transaction!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @TransactionalEventListener
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void generateVerificationToken(CustomerCreated customerCreated) {
        final Long customerId = customerCreated.getId();
        final Customer customer = customerJpaRepository.findById(customerId).get();
        customer.setVerificationToken(
            String.valueOf(
                Objects.hashCode(customer)
            )
        ); //Dummy

        customerJpaRepository.save(customer);
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;i.e execute this listener after the original transaction (AFTER_COMMIT), and open a new Transaction (REQUIRES NEW)!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule of Thumb&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use @EventListener if you want this work to be a part of the enclosing/original transaction&lt;/li&gt;
&lt;li&gt;Use @TransactionalEventListener (default phase = AFTER_COMMIT) if you want this work to be done after the enclosing transaction is committed. But remember, you won’t be able to do any DB work since the original transaction has already committed. Use this to do other work — send emails, send events to a messaging queue, etc.&lt;/li&gt;
&lt;li&gt;Use @TransactionalEventListener (default phase = AFTER_COMMIT) along with @Transactional(propagation=REQUIRES_NEW) to do DB work in a new transaction after the enclosing/original transaction is committed!&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>springboot</category>
      <category>springevents</category>
      <category>java</category>
      <category>transactional</category>
    </item>
    <item>
      <title>The Observer design pattern</title>
      <dc:creator>Ishan Soni</dc:creator>
      <pubDate>Wed, 20 Dec 2023 11:56:53 +0000</pubDate>
      <link>https://dev.to/ishansoni22/the-observer-design-pattern-51bi</link>
      <guid>https://dev.to/ishansoni22/the-observer-design-pattern-51bi</guid>
      <description>&lt;p&gt;The Observer design pattern is also called Publish-Subscribe pattern and is the core principle of the Reactive paradigm (PUSH in favor of PULL).&lt;/p&gt;

&lt;h4&gt;
  
  
  Design Considerations
&lt;/h4&gt;

&lt;p&gt;There are many &lt;strong&gt;observers&lt;/strong&gt; (subscribers that are interested in some data) that are observing a &lt;strong&gt;publisher&lt;/strong&gt; (which maintains that data). Observers register themselves to the publisher. The publisher can then push data to these observers, since it maintains a list of subscribed observers (PUSH!), whenever there is a change in the publisher’s state. &lt;/p&gt;

&lt;p&gt;This pattern defines a one-to-many dependency between objects such that when the state of one object changes, all its dependents are notified and updated automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: You are working on the user microservice. When a user is created, the following activities have to be performed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send out a welcome email to the user.&lt;/li&gt;
&lt;li&gt;Generate and send a verification token to this user using 
which they can verify their account.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One way of structuring our code would be:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void createUser(CreateUserCommand command) {
    //omitted - code to create and save a user
    sendWelcomeEmail();
    generateAndSendVerificationToken();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This violates both the &lt;strong&gt;S&lt;/strong&gt; and &lt;strong&gt;O&lt;/strong&gt; of the &lt;strong&gt;SOLID&lt;/strong&gt; principles. Your UserService is doing too much work and is not extensible. Let's assume, as part of the user creation process, we now need to create an Avatar for the user as well. Your code would then become:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void createUser(CreateUserCommand command) {
    //omitted - code to create and save a user
    sendWelcomeEmail();
    generateAndSendVerificationToken();
    generateAvatar();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Instead, let's try to use the Observer design pattern
&lt;/h4&gt;

&lt;p&gt;The data&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public record UserCreated(String userId, String email, String firstName, String lastName) {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Publisher interface&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface Publisher&amp;lt;T&amp;gt; {
    void subscribe(Subscriber&amp;lt;T&amp;gt; subscriber);
    void unsubscribe(Subscriber&amp;lt;T&amp;gt; subscriber);
    void publish(T data);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Subscriber interface&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface Subscriber&amp;lt;T&amp;gt; {
    void next(T data);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's create the concrete Publisher and Subscribers&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class UserPublisher implements Publisher&amp;lt;UserCreated&amp;gt; {

    private final List&amp;lt;Subscriber&amp;lt;UserCreated&amp;gt;&amp;gt; subscribers = new ArrayList&amp;lt;&amp;gt;();

    @Override
    public void subscribe(Subscriber&amp;lt;UserCreated&amp;gt; subscriber) {
        subscribers.add(subscriber);
    }

    @Override
    public void unsubscribe(Subscriber&amp;lt;UserCreated&amp;gt; subscriber) {
        subscribers.remove(subscriber);
    }

    @Override
    public void publish(UserCreated data) {
        subscribers.forEach(s -&amp;gt; s.next(data));
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class UserCreatedNotifier implements Subscriber&amp;lt;UserCreated&amp;gt;{

    @Override
    public void next(UserCreated data) {
        System.out.println("Sending email to " + data.email());
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class VerificationTokenGenerator implements Subscriber&amp;lt;UserCreated&amp;gt; {

    @Override
    public void next(UserCreated data) {
        System.out.println("Generating verification token and sending to " + data.email());
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's test it out&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void createUser(CreateUserCommand command) {
    //... User creation code

    UserPublisher userPublisher = new UserPublisher();

    UserCreatedNotifier userCreatedNotifier = new UserCreatedNotifier();
    VerificationTokenGenerator verificationTokenGenerator = new VerificationTokenGenerator();

    userPublisher.subscribe(userCreatedNotifier);
    userPublisher.subscribe(verificationTokenGenerator);

    userPublisher.publish(new UserCreated("user-1", "user1@gmail.com", "User", "1"));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PzjBk641--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/psze6a52wja0j89m1y0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PzjBk641--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/psze6a52wja0j89m1y0m.png" alt="Output" width="486" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's work on the Avatar generation process and see how extensible this design pattern is:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class AvatarGenerator implements Subscriber&amp;lt;UserCreated&amp;gt; {
    @Override
    public void next(UserCreated data) {
        System.out.println("Generating avatar for user " + data.email());
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//same code as before    
AvatarGenerator avatarGenerator = new AvatarGenerator();
userPublisher.subscribe(avatarGenerator);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gx5YqlQh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lossdt0ym80fqueeyr7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gx5YqlQh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lossdt0ym80fqueeyr7c.png" alt="Output" width="480" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Advantages
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Loose Coupling&lt;/strong&gt;: When two objects are loosely coupled, they can interact but have a very little knowledge of each other. The observer design pattern provides an object design where the publisher and subscribers are loosely coupled:&lt;br&gt;
The only thing a publisher knows about a subscriber is that it implements the subscriber interface. We can add new/remove subscribers at any time since the only thing the publisher depends on is a list of objects that implement the subscriber interface.&lt;/p&gt;

&lt;p&gt;Changes to either publisher or subscribers will not affect each other.&lt;/p&gt;

</description>
      <category>designpatterns</category>
      <category>java</category>
      <category>observerdesignpattern</category>
      <category>pubsub</category>
    </item>
  </channel>
</rss>
