Sunday, 15 June 2025

Hibernate, JPA, Microservices and Kafka annotations

 ๐Ÿ”„ Hibernate Annotations 

These annotations are used for Object-Relational Mapping (ORM) in Hibernate and JPA-based applications.


1. @Entity

  • What@Entity is a JPA (Java Persistence API) annotation used to mark a class as a persistent entity, meaning the class is mapped to a table in a relational database. Hibernate uses this annotation to recognize which classes should be stored in the database..

  • Why: Enables Hibernate to persist the object in the DB.

  • Where: On top of POJO class.

  • When: When creating a model mapped to a database table.

import javax.persistence.*; @Entity @Table(name = "employees") // optional: overrides default table name public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "emp_name", nullable = false) private String name; private String department; // Getters and Setters }


2. @Table(name = "table_name")

  • What@Table is a JPA annotation used to explicitly specify the name of the database table that an @Entity class should map to.

    If this annotation is not used, the table name is assumed to be the same as the entity class name by default.

  • Why: To map the class to a specific table.

  • Where: On top of the entity class.

  • When: When table name differs from the class name.

import javax.persistence.*; @Entity @Table(name = "employees") // Maps this class to 'employees' table public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "emp_name", nullable = false) private String name; private String department; // Getters and setters }


3. @Id

  • What: Marks the primary key of the entity.

  • Why: Hibernate requires a unique identifier for persistence.

  • Where: On a field of the entity class.

  • When: For any field acting as a primary key.

@Id
private Long id;

4. @GeneratedValue

  • What: Specifies how the primary key is generated.

  • Why: For auto-increment or sequence-based IDs.

  • Where: Along with @Id.

  • When: When the DB should generate IDs.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;

5. @Column

  • What: Maps a field to a table column.

  • Why: To customize the column mapping (name, length, nullable).

  • Where: On entity fields.

  • When: For customization or matching different DB column names.

@Column(name = "first_name", nullable = false, length = 100)
private String firstName;

6. @OneToOne, @OneToMany, @ManyToOne, @ManyToMany

๐Ÿ”น What:

These annotations define associations between two entity classes. They instruct Hibernate (and JPA) on how to map object relationships to database relationships, especially foreign keys.


๐Ÿ“Œ Why:

In relational databases:

  • Entities are linked using foreign keys.

  • In Java, we use references or collections.

These annotations bridge the gap between object-oriented programming and relational models (ORM - Object Relational Mapping).


๐Ÿงญ Where:

  • Place on fields inside entity classes.

  • Often combined with @JoinColumn, @JoinTable, mappedBy, cascade, and fetch.


๐Ÿ•’ When to Use:

Use these when your domain model has real-world relationships:

  • Employee belongs to one Department: @ManyToOne

  • Department has many Employees: @OneToMany

  • A User has one Profile: @OneToOne

  • Students can enroll in many Courses: @ManyToMany


๐Ÿ“˜ Detailed Annotations


@OneToOne

➤ Description:

Defines a one-to-one relationship between two entities.

➤ Example:

@Entity public class User { @Id private Long id; @OneToOne @JoinColumn(name = "profile_id") // foreign key in User table private Profile profile; }

➤ Use Case:

  • A User has one Profile.

  • Place @OneToOne on the owning side (where FK exists).


@OneToMany

➤ Description:

Defines a one-to-many relationship — one entity is related to multiple instances of another entity.

➤ Example:

@Entity public class Department { @Id private Long id; @OneToMany(mappedBy = "department", cascade = CascadeType.ALL) private List<Employee> employees; }

Note: mappedBy tells Hibernate that the foreign key resides in the Employee entity.


@ManyToOne

➤ Description:

Defines a many-to-one relationship — many instances relate to one instance of another entity.

➤ Example:

@Entity public class Employee { @Id private Long id; @ManyToOne @JoinColumn(name = "department_id") // FK in Employee table private Department department; }

Typically used in the child or owning side of a bidirectional relationship.


@ManyToMany

➤ Description:

Defines a many-to-many relationship — both sides can relate to multiple instances of the other.

➤ Example:

@Entity public class Student { @Id private Long id; @ManyToMany @JoinTable( name = "student_course", joinColumns = @JoinColumn(name = "student_id"), inverseJoinColumns = @JoinColumn(name = "course_id") ) private List<Course> courses; }

This creates a join table student_course with 2 foreign keys.


๐Ÿ“Œ Additional Attributes

  • mappedBy: Indicates the field that owns the relationship (used in bidirectional mapping).

  • cascade: Enables cascading operations like PERSIST, MERGE, REMOVE.

  • fetch: Defines loading strategy (FetchType.LAZY or FetchType.EAGER).


๐Ÿงช Summary Table

AnnotationCardinalityExample Use Case
@OneToOne1 ↔ 1User ↔ Profile
@OneToMany1 ↔ ManyDepartment ↔ Employees
@ManyToOneMany ↔ 1Employee ↔ Department
@ManyToManyMany ↔ ManyStudent ↔ Course

7. @JoinColumn

  • What: Specifies the foreign key column.

  • Why: For explicitly naming and customizing joins.

  • Where: On relational fields.

  • When: In associations.


@ManyToOne @JoinColumn(name = "dept_id") private Department department;

8. @Lob

  • What: Maps large objects like BLOB or CLOB.

  • Why: For storing large text/images.

  • Where: On entity fields.

  • When: For binary or large content.


9. @Temporal

  • What: Maps java.util.Date or Calendar to SQL date/time.

  • Why: To handle date types properly.

  • Where: On date/time fields.

  • When: When storing time-sensitive data.


@Temporal(TemporalType.DATE) private Date dob;

10. @Transient

  • What: Excludes a field from persistence.

  • Why: For non-persistent calculated or helper fields.

  • Where: On entity fields.

  • When: When field should not be saved to DB.


⚙️ Kafka Annotations in Spring Boot

1.@KafkaListener

What: @KafkaListener is a Spring annotation used to designate a method as a Kafka message consumer.

Why:
  • Automates the message consumption process from a Kafka topic.

  • Eliminates the need to manually poll the topic.

  • Supports batch consumption, filtering, error handling, concurrency, etc.


Where:

  • Applied to a method in a Spring-managed bean (typically a @Service or @Component class).


When:

  • Used when you want to consume messages from a Kafka topic, typically in event-driven microservices.


๐Ÿงฉ Example:

import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Service; @Service public class OrderConsumerService { @KafkaListener(topics = "orders", groupId = "order-service") public void consume(String message) { System.out.println("Received: " + message); } }

2. @KafkaHandler

What

@KafkaHandler is used to handle multiple message types (i.e., method overloading) within a single Kafka consumer class that is annotated with class-level @KafkaListener.


Why

  • Kafka normally maps one listener method to one topic. But when you want to consume different object types from the same topic, @KafkaHandler allows handling them in separate methods.

  • This is useful for polymorphic deserialization — messages are deserialized to different POJOs based on type.


Where

  • Used on multiple methods inside a class that has a class-level @KafkaListener.

  • Each method has a unique parameter type (POJO, String, etc.).


When

Use @KafkaHandler:

  • When you want a single Kafka consumer to process different message types from a single topic.

  • When working with message classes like OrderCreated, OrderCancelled, etc., all published to the same topic.

Example

Suppose you have a topic named "events" that sends different message types:

json

// 1st message { "type": "email", "to": "user@example.com", "subject": "Welcome" } // 2nd message { "type": "sms", "to": "+911234567890", "content": "Your OTP is 1234" }

✅ POJO Classes

java

public class EmailNotification { private String to; private String subject; // getters and setters } public class SMSNotification { private String to; private String content; // getters and setters }

✅ Kafka Consumer with @KafkaHandler

java

@KafkaListener(topics = "events", groupId = "notification-group") public class NotificationHandler { @KafkaHandler public void handleEmail(EmailNotification email) { System.out.println("Received email: " + email.getSubject()); } @KafkaHandler public void handleSMS(SMSNotification sms) { System.out.println("Received SMS: " + sms.getContent()); } // Fallback handler @KafkaHandler(isDefault = true) public void handleUnknown(Object obj) { System.out.println("Unknown message: " + obj); } }

๐Ÿ”ง Required Configuration for Deserialization

You must configure a message converter to automatically convert JSON messages to their respective Java objects:

java

@Bean public RecordMessageConverter messageConverter() { return new StringJsonMessageConverter(); }

3. @SendTo

  • What: Sends reply to another topic.

  • Why: For request-reply patterns.

  • Where: On Kafka consumer methods.

  • When: When response is needed after processing.


@KafkaListener(topics = "request-topic") @SendTo("response-topic") public String handleRequest(String request) { return "Processed: " + request; }

4. @EnableKafka

What

@EnableKafka is a Spring annotation that enables support for Kafka listener annotations like @KafkaListener, @KafkaHandler, etc.


Why

By default, Spring Boot does not scan or activate Kafka listener infrastructure.

  • This annotation registers the Kafka listener container factory and related beans in the Spring context.

  • Without @EnableKafka, your @KafkaListener methods will be ignored and not invoked, even if correctly defined.


Where

  • On a Java configuration class (annotated with @Configuration).

  • Typically, the class where you define Kafka-related beans like ConsumerFactory, ConcurrentKafkaListenerContainerFactory, etc.


When

  • Once per application, typically at startup.

  • Required only if you use annotation-based Kafka listeners, like @KafkaListener.


✅ Example

๐Ÿ“„ Kafka Configuration Class

import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka; @Configuration @EnableKafka public class KafkaConfig { // Optional: Kafka consumer or producer bean definitions }

This is the minimum requirement to allow Spring to detect and activate Kafka listener methods.


๐Ÿง  Behind the Scenes

Internally, @EnableKafka:

  • Registers a KafkaListenerAnnotationBeanPostProcessor.

  • That bean processes all @KafkaListener annotations during application context initialization.

  • It then creates Kafka listener containers for each listener method.

Without this setup, message consumption won't happen even if topics are available and properly configured.


๐Ÿ”ง Typically used alongside

You usually combine @EnableKafka with Kafka consumer configurations like:

@Bean
public ConsumerFactory<String, String> consumerFactory() { Map<String, Object> props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); return new DefaultKafkaConsumerFactory<>(props); } @Bean public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(consumerFactory()); return factory; }

✅ Summary Table

AttributeDescription
WhatEnables Spring to detect and process Kafka-related annotations
WhyRequired to make @KafkaListener, @KafkaHandler work
WhereIn a @Configuration class
WhenOnce per application — at app configuration/startup

๐Ÿงฑ Microservices Related Annotations


1. @EnableDiscoveryClient

  • What@EnableDiscoveryClient is a Spring Cloud annotation that enables service registration and discovery capabilities in a Spring Boot application.

    It tells the application to register itself with a Discovery Server like:

    • Eureka

    • Consul

    • Zookeeper

  • Why: To register microservices with Eureka/Consul.

  • Where: On Spring Boot app class.

  • When: When using service discovery.

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

2. @LoadBalanced

  • What@LoadBalanced is a Spring Cloud annotation used to enable client-side load balancing on a RestTemplate or WebClient. Enables client-side load balancing using Ribbon.

It allows the client to resolve service names (like http://order-service) into actual instances using the service discovery registry (like Eureka or Consul).

  • Why: To distribute REST calls across service instances.

In microservices, multiple instances of the same service may be running for scalability and resilience. Instead of hardcoding IPs or ports, you use the service name:

java
restTemplate.getForObject("http://order-service/orders", String.class);
  • Where: On RestTemplate bean.

  • When: In clients calling other microservices.

@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }

3. @EnableFeignClients

  • What: Enables use of Feign declarative REST clients.

@EnableFeignClients is a Spring Cloud annotation that enables the use of Feign, a declarative HTTP client, in your Spring Boot application.

It scans your project for interfaces annotated with @FeignClient and auto-generates implementations that handle HTTP requests to other microservices.

  • Why: To simplify inter-service HTTP calls.

  • Where: On main Spring Boot app or config class.

  • When: When using Feign clients.

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

4. @FeignClient(name = "user-service")

What

@FeignClient is a Spring Cloud annotation used to declare an interface as a REST client for another microservice.

It tells Spring to generate a proxy implementation of the interface that:

  • Automatically resolves the service name (user-service) using a discovery client (like Eureka)

  • Sends HTTP requests to the corresponding service

  • Handles serialization and deserialization of request/response data


Why

Using @FeignClient simplifies HTTP communication between microservices by:

  • Eliminating manual RestTemplate setup

  • Avoiding repetitive HTTP call code

  • Supporting fallback mechanisms with Resilience4j or Hystrix

  • Integrating easily with Spring Cloud components (like Eureka, Ribbon/Spring LoadBalancer)


Where

Place @FeignClient(name = "user-service"):

  • On a Java interface

  • That declares methods for calling REST endpoints on the user-service


When

Use @FeignClient:

  • When your service calls another microservice over HTTP

  • When using Eureka/Consul for dynamic service discovery

  • When you want clean, type-safe, declarative HTTP calls


✅ Example Usage

Let’s say your order-service wants to fetch user info from user-service.


1. ๐Ÿ“„ Enable Feign Clients

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

2. ๐Ÿ“„ Define Feign Client Interface

@FeignClient(name = "user-service") // name must match the Eureka service ID public interface UserClient { @GetMapping("/users/{id}") User getUser(@PathVariable("id") Long id); 
}

5. @CircuitBreaker, @Retry, @RateLimiter, @Bulkhead

  • What: Provide fault tolerance and resilience.

  • Why: To handle microservice failures gracefully.

  • Where: On service methods.

  • When: For timeout handling, retries, etc.

(from Resilience4j, a lightweight fault tolerance library)

These annotations help microservices stay resilient under failure, load, or latency by offering fault tolerance patterns like circuit breakers, retries, rate limiting, and thread isolation.


✅ A. @CircuitBreaker

๐Ÿ”น What

Monitors the failure rate of a method and opens the circuit if the failure threshold is breached. Prevents further calls until the service is considered healthy again.

๐Ÿ”น Why

To avoid repeated failed calls to a service that’s down or slow, improving system stability and responsiveness.

๐Ÿ”น Where

On service methods that call remote services (e.g., REST APIs via Feign or RestTemplate).

๐Ÿ”น When

Use it when calling unreliable downstream services.

Example:

@CircuitBreaker(name = "userService", fallbackMethod = "fallback")
public String callUserService() { return restTemplate.getForObject("http://user-service/users/1", String.class); } public String fallback(Throwable ex) { return "User service is down. Please try later."; }

๐Ÿ”น Configuration (in application.yml)

yaml
resilience4j.circuitbreaker:
instances: userService: registerHealthIndicator: true slidingWindowSize: 10 failureRateThreshold: 50 waitDurationInOpenState: 5s

✅ B. @Retry

๐Ÿ”น What

Automatically retries a failed method a fixed number of times.

๐Ÿ”น Why

To handle temporary failures (e.g., network issues, service timeouts) without failing immediately.

๐Ÿ”น Where

On methods calling remote services.

๐Ÿ”น When

Use when there’s a high probability of transient failure that can be resolved with a retry.

Example:

@Retry(name = "userService", fallbackMethod = "fallback")
public String callUserService() { return restTemplate.getForObject("http://user-service/users/1", String.class); } public String fallback(Throwable t) { return "Temporary issue. Please try again later."; }

๐Ÿ”น Configuration:

yaml
resilience4j.retry:
instances: userService: maxAttempts: 3 waitDuration: 1s

✅ C. @RateLimiter

๐Ÿ”น What

Limits the rate of method invocations.

๐Ÿ”น Why

To protect your service from being overwhelmed by too many requests in a short period.

๐Ÿ”น Where

On methods that can tolerate throttling, such as public APIs or heavy DB operations.

๐Ÿ”น When

Use when you need to control traffic to prevent overloading the system.

Example:

java
@RateLimiter(name = "userService", fallbackMethod = "fallback") public String callUserService() { return restTemplate.getForObject("http://user-service/users/1", String.class); } public String fallback(RequestNotPermitted ex) { return "Too many requests. Try again later."; }

๐Ÿ”น Configuration:

yaml
resilience4j.ratelimiter: instances: userService: limitForPeriod: 5 limitRefreshPeriod: 1s

✅ D. @Bulkhead

๐Ÿ”น What

Limits the number of concurrent calls to a method, isolating failures.

๐Ÿ”น Why

To prevent resource exhaustion (e.g., thread or memory) by restricting how many requests can be processed in parallel.

๐Ÿ”น Where

On high-latency or high-resource methods.

๐Ÿ”น When

Use when you need to isolate components to contain failure to a specific section of the system.

Example:

@Bulkhead(name = "userService", fallbackMethod = "fallback")
public String callUserService() { return restTemplate.getForObject("http://user-service/users/1", String.class); } public String fallback(Throwable t) { return "Service busy. Please try again later."; }

๐Ÿ”น Configuration:

yaml
resilience4j.bulkhead:
instances: userService: maxConcurrentCalls: 5 maxWaitDuration: 0

๐Ÿ”„ Summary Table

AnnotationPurposeUse Case
@CircuitBreakerPrevents repeated failed callsUnstable or flaky downstream service
@RetryRetries failed calls automaticallyTemporary issues (e.g., timeouts)
@RateLimiterControls number of calls per secondPublic APIs, database-intensive operations
@BulkheadLimits concurrent executionsProtect system from thread starvation

๐Ÿ› ️ Dependencies

To use these annotations, add the following dependency:

xml
<dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-boot2</artifactId> </dependency>