You are viewing a preview of this lesson. Sign in to start learning
Back to Java

Enterprise Java

Build production-ready applications with modern frameworks and best practices

Enterprise Java

Master Enterprise Java with free flashcards and spaced repetition practice covering Java EE architecture, dependency injection, RESTful web services, and JPA persistence. This lesson explores the frameworks and patterns that power large-scale business applications, equipping you with the skills to build robust, scalable enterprise systems.

Welcome to Enterprise Java πŸ’Ό

Welcome to the world of Enterprise Java! If you've been building standalone Java applications or simple web projects, you're about to level up. Enterprise Java (formerly Java EE, now Jakarta EE) is the industry-standard platform for building large-scale, distributed, transactional, and highly available applications that power banks, airlines, e-commerce giants, and Fortune 500 companies.

In this lesson, you'll discover the core technologies that make enterprise applications tick: servlets and JSPs for web presentation, Enterprise JavaBeans (EJB) for business logic, Java Persistence API (JPA) for database operations, Java Messaging Service (JMS) for asynchronous communication, and JAX-RS for RESTful web services. You'll also explore modern frameworks like Spring and Jakarta EE that simplify enterprise development.

πŸ’‘ Did you know? Enterprise Java applications can handle millions of concurrent users and process billions of transactions daily. Companies like LinkedIn, Twitter (in its early days), and eBay have all relied on Java enterprise technologies to scale their platforms.


Core Concepts of Enterprise Java πŸ—οΈ

1. Multi-Tier Architecture πŸ”Ί

Enterprise applications follow a layered architecture to separate concerns and improve maintainability:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         PRESENTATION LAYER              β”‚
β”‚  (Servlets, JSP, JSF, REST APIs)        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         BUSINESS LOGIC LAYER            β”‚
β”‚  (EJB, Spring Beans, Services)          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         PERSISTENCE LAYER               β”‚
β”‚  (JPA, Hibernate, JDBC)                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         DATABASE LAYER                  β”‚
β”‚  (MySQL, PostgreSQL, Oracle)            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Each layer has a specific responsibility:

  • Presentation Layer: Handles user interaction, renders UI, processes HTTP requests
  • Business Logic Layer: Implements business rules, validation, workflows
  • Persistence Layer: Manages data access and database operations
  • Database Layer: Stores and retrieves data

2. Dependency Injection (DI) πŸ’‰

Dependency Injection is a design pattern where objects receive their dependencies from an external source rather than creating them internally. This promotes loose coupling and testability.

Without DI:

public class OrderService {
    private PaymentProcessor processor = new PaymentProcessor(); // Tight coupling!
    
    public void processOrder(Order order) {
        processor.process(order.getPayment());
    }
}

With DI (using annotations):

@Service
public class OrderService {
    @Inject // Or @Autowired in Spring
    private PaymentProcessor processor; // Injected by container!
    
    public void processOrder(Order order) {
        processor.process(order.getPayment());
    }
}

The DI container (Spring IoC, CDI in Jakarta EE) manages object lifecycles and injects dependencies automatically.

🧠 Memory device: Think of DI as a restaurant kitchen - instead of each chef buying their own ingredients (creating dependencies), a central supplier (container) delivers what each chef needs.

3. Enterprise JavaBeans (EJB) 🫘

EJBs are server-side components that encapsulate business logic. They provide built-in services like transactions, security, and concurrency management.

Types of EJBs:

Type Purpose Example Use Case
Session Beans Execute business logic OrderProcessingBean, UserAuthenticationBean
Message-Driven Beans Process asynchronous messages EmailNotificationBean, LogProcessorBean
Entity Beans (deprecated) Represent database entities Replaced by JPA entities

Session Bean Example:

@Stateless // Container manages lifecycle
public class AccountService {
    @PersistenceContext
    private EntityManager em;
    
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account from = em.find(Account.class, fromId);
        Account to = em.find(Account.class, toId);
        from.debit(amount);
        to.credit(amount);
        // Transaction committed automatically!
    }
}

Stateless vs Stateful:

  • @Stateless: No conversational state between method calls (scalable, pooled)
  • @Stateful: Maintains state across multiple client interactions (shopping cart)

4. Java Persistence API (JPA) πŸ’Ύ

JPA is the standard for Object-Relational Mapping (ORM) in Java, allowing you to work with databases using Java objects instead of SQL.

JPA Entity Example:

@Entity
@Table(name = "customers")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, length = 100)
    private String name;
    
    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
    private List<Order> orders;
    
    // Getters and setters...
}

EntityManager Operations:

@Stateless
public class CustomerRepository {
    @PersistenceContext
    private EntityManager em;
    
    public Customer findById(Long id) {
        return em.find(Customer.class, id);
    }
    
    public void save(Customer customer) {
        em.persist(customer);
    }
    
    public List<Customer> findByName(String name) {
        return em.createQuery(
            "SELECT c FROM Customer c WHERE c.name LIKE :name", Customer.class)
            .setParameter("name", "%" + name + "%")
            .getResultList();
    }
}

JPQL (Java Persistence Query Language) is an object-oriented query language similar to SQL but operates on entity objects:

String jpql = "SELECT o FROM Order o WHERE o.customer.id = :customerId AND o.total > :minTotal";
List<Order> orders = em.createQuery(jpql, Order.class)
    .setParameter("customerId", 123L)
    .setParameter("minTotal", new BigDecimal("100.00"))
    .getResultList();

5. RESTful Web Services with JAX-RS 🌐

JAX-RS (Java API for RESTful Web Services) enables you to build REST APIs using annotations.

REST Endpoint Example:

@Path("/products")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ProductResource {
    @Inject
    private ProductService productService;
    
    @GET
    public List<Product> getAllProducts() {
        return productService.findAll();
    }
    
    @GET
    @Path("/{id}")
    public Response getProduct(@PathParam("id") Long id) {
        Product product = productService.findById(id);
        if (product == null) {
            return Response.status(Status.NOT_FOUND).build();
        }
        return Response.ok(product).build();
    }
    
    @POST
    public Response createProduct(Product product) {
        productService.save(product);
        return Response.status(Status.CREATED)
            .entity(product)
            .build();
    }
    
    @PUT
    @Path("/{id}")
    public Response updateProduct(@PathParam("id") Long id, Product product) {
        product.setId(id);
        productService.update(product);
        return Response.ok(product).build();
    }
    
    @DELETE
    @Path("/{id}")
    public Response deleteProduct(@PathParam("id") Long id) {
        productService.delete(id);
        return Response.noContent().build();
    }
}

HTTP Method Mapping:

HTTP Method JAX-RS Annotation Purpose Idempotent?
GET @GET Retrieve resources βœ… Yes
POST @POST Create new resource ❌ No
PUT @PUT Update entire resource βœ… Yes
PATCH @PATCH Partial update ❌ No
DELETE @DELETE Remove resource βœ… Yes

6. Transaction Management πŸ”„

Enterprise applications require ACID transactions (Atomicity, Consistency, Isolation, Durability).

Container-Managed Transactions (CMT):

@Stateless
public class BankingService {
    @PersistenceContext
    private EntityManager em;
    
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void transferMoney(Long fromAccount, Long toAccount, BigDecimal amount) {
        Account from = em.find(Account.class, fromAccount);
        Account to = em.find(Account.class, toAccount);
        
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        
        // If any exception occurs, entire transaction rolls back!
    }
}

Transaction Attributes:

  • REQUIRED: Join existing transaction or create new one (default)
  • REQUIRES_NEW: Always create new transaction, suspend existing
  • MANDATORY: Must be called within transaction, else exception
  • SUPPORTS: Join transaction if exists, else run without
  • NOT_SUPPORTED: Suspend transaction, run without
  • NEVER: Exception if transaction exists

7. Spring Framework πŸƒ

Spring is the most popular enterprise Java framework, offering a comprehensive programming model.

Spring Core Annotations:

@Configuration
public class AppConfig {
    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        return ds;
    }
}

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional
    public Order createOrder(Order order) {
        return orderRepository.save(order);
    }
}

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findByCustomerId(Long customerId);
}

@RestController
@RequestMapping("/api/orders")
public class OrderController {
    @Autowired
    private OrderService orderService;
    
    @GetMapping("/{id}")
    public ResponseEntity<Order> getOrder(@PathVariable Long id) {
        return ResponseEntity.ok(orderService.findById(id));
    }
}

Spring Boot simplifies configuration with auto-configuration and embedded servers:

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

Practical Examples πŸ”§

Example 1: Complete REST API with JPA

Entity:

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String author;
    private BigDecimal price;
    
    // Constructors, getters, setters...
}

Repository:

@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
    List<Book> findByAuthor(String author);
    List<Book> findByPriceLessThan(BigDecimal maxPrice);
}

Service:

@Service
@Transactional
public class BookService {
    @Autowired
    private BookRepository bookRepository;
    
    public List<Book> getAllBooks() {
        return bookRepository.findAll();
    }
    
    public Book getBookById(Long id) {
        return bookRepository.findById(id)
            .orElseThrow(() -> new BookNotFoundException(id));
    }
    
    public Book createBook(Book book) {
        return bookRepository.save(book);
    }
}

REST Controller:

@RestController
@RequestMapping("/api/books")
public class BookController {
    @Autowired
    private BookService bookService;
    
    @GetMapping
    public List<Book> getAllBooks() {
        return bookService.getAllBooks();
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<Book> getBook(@PathVariable Long id) {
        return ResponseEntity.ok(bookService.getBookById(id));
    }
    
    @PostMapping
    public ResponseEntity<Book> createBook(@RequestBody Book book) {
        Book created = bookService.createBook(book);
        return ResponseEntity.status(HttpStatus.CREATED).body(created);
    }
}

Example 2: Message-Driven Bean for Async Processing

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationType", 
                              propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "destination", 
                              propertyValue = "queue/OrderQueue")
})
public class OrderProcessorMDB implements MessageListener {
    @Inject
    private OrderService orderService;
    
    @Override
    public void onMessage(Message message) {
        try {
            if (message instanceof TextMessage) {
                String orderId = ((TextMessage) message).getText();
                orderService.processOrder(Long.parseLong(orderId));
                System.out.println("Processed order: " + orderId);
            }
        } catch (JMSException e) {
            throw new RuntimeException(e);
        }
    }
}

Sending Messages:

@Stateless
public class OrderSubmissionService {
    @Resource(mappedName = "java:/ConnectionFactory")
    private ConnectionFactory connectionFactory;
    
    @Resource(mappedName = "java:/queue/OrderQueue")
    private Queue orderQueue;
    
    public void submitOrder(Long orderId) {
        try (Connection connection = connectionFactory.createConnection();
             Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE)) {
            MessageProducer producer = session.createProducer(orderQueue);
            TextMessage message = session.createTextMessage(orderId.toString());
            producer.send(message);
        } catch (JMSException e) {
            throw new RuntimeException(e);
        }
    }
}

Example 3: Custom Exception Handling in REST APIs

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.NOT_FOUND.value(),
            ex.getMessage(),
            System.currentTimeMillis()
        );
        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.INTERNAL_SERVER_ERROR.value(),
            "An unexpected error occurred",
            System.currentTimeMillis()
        );
        return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Example 4: Bean Validation with Hibernate Validator

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @NotBlank(message = "Username is required")
    @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
    private String username;
    
    @Email(message = "Email must be valid")
    @NotBlank(message = "Email is required")
    private String email;
    
    @Min(value = 18, message = "Age must be at least 18")
    private int age;
    
    // Getters and setters...
}

@RestController
@RequestMapping("/api/users")
public class UserController {
    @PostMapping
    public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
        // If validation fails, throws MethodArgumentNotValidException
        return ResponseEntity.ok(userService.save(user));
    }
}

Common Mistakes ⚠️

1. Forgetting to Specify Transaction Boundaries

// ❌ WRONG: No transaction management
public class OrderService {
    @PersistenceContext
    private EntityManager em;
    
    public void createOrder(Order order) {
        em.persist(order); // May not be persisted!
    }
}

// βœ… RIGHT: Explicit transaction
@Transactional
public void createOrder(Order order) {
    em.persist(order);
}

2. N+1 Query Problem with Lazy Loading

// ❌ WRONG: Causes multiple queries
@Entity
public class Order {
    @OneToMany(fetch = FetchType.LAZY) // Default
    private List<OrderItem> items;
}

List<Order> orders = orderRepository.findAll();
for (Order order : orders) {
    System.out.println(order.getItems().size()); // Triggers query for each order!
}

// βœ… RIGHT: Use JOIN FETCH
String jpql = "SELECT o FROM Order o JOIN FETCH o.items";
List<Order> orders = em.createQuery(jpql, Order.class).getResultList();

3. Mixing Stateful and Stateless Session Beans Incorrectly

// ❌ WRONG: Using @Stateless with conversational state
@Stateless
public class ShoppingCartBean {
    private List<Item> cart = new ArrayList<>(); // Lost between calls!
}

// βœ… RIGHT: Use @Stateful for conversational state
@Stateful
public class ShoppingCartBean {
    private List<Item> cart = new ArrayList<>();
}

4. Not Handling OptionalEmpty Properly

// ❌ WRONG: Can throw NoSuchElementException
Book book = bookRepository.findById(id).get();

// βœ… RIGHT: Handle absence
Book book = bookRepository.findById(id)
    .orElseThrow(() -> new BookNotFoundException(id));

5. Exposing Entities Directly in REST APIs

// ❌ WRONG: Exposes internal structure, causes lazy loading issues
@GetMapping("/{id}")
public Customer getCustomer(@PathVariable Long id) {
    return customerRepository.findById(id).get();
}

// βœ… RIGHT: Use DTOs (Data Transfer Objects)
@GetMapping("/{id}")
public CustomerDTO getCustomer(@PathVariable Long id) {
    Customer customer = customerRepository.findById(id).get();
    return new CustomerDTO(customer);
}

Key Takeaways 🎯

βœ… Enterprise Java provides a comprehensive platform for building scalable, transactional, distributed applications

βœ… Multi-tier architecture separates presentation, business logic, and persistence layers

βœ… Dependency Injection promotes loose coupling and testability through inversion of control

βœ… JPA provides object-relational mapping, eliminating most manual SQL code

βœ… EJBs and Spring Beans encapsulate business logic with container-managed services

βœ… JAX-RS enables creation of RESTful web services using annotations

βœ… Transaction management ensures ACID properties for database operations

βœ… Spring Framework offers a modern, annotation-driven approach to enterprise development

βœ… Bean validation enforces data constraints declaratively

βœ… Message-driven beans enable asynchronous, decoupled communication


πŸ“š Further Study

  1. Jakarta EE Official Documentation: https://jakarta.ee/specifications/
  2. Spring Framework Documentation: https://spring.io/projects/spring-framework
  3. Baeldung Enterprise Java Tutorials: https://www.baeldung.com/spring-tutorial

πŸ“‹ Quick Reference Card

@EntityMarks a class as a JPA entity
@StatelessStateless session bean (no conversational state)
@Inject / @AutowiredDependency injection
@TransactionalMarks method/class as transactional
@PathDefines REST endpoint path
@GET/@POST/@PUT/@DELETEHTTP method mapping
@PersistenceContextInjects EntityManager
EntityManagerJPA interface for database operations
JPQLObject-oriented query language for JPA
@MessageDrivenMessage-driven bean for async processing