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
- Jakarta EE Official Documentation: https://jakarta.ee/specifications/
- Spring Framework Documentation: https://spring.io/projects/spring-framework
- Baeldung Enterprise Java Tutorials: https://www.baeldung.com/spring-tutorial
π Quick Reference Card
| @Entity | Marks a class as a JPA entity |
| @Stateless | Stateless session bean (no conversational state) |
| @Inject / @Autowired | Dependency injection |
| @Transactional | Marks method/class as transactional |
| @Path | Defines REST endpoint path |
| @GET/@POST/@PUT/@DELETE | HTTP method mapping |
| @PersistenceContext | Injects EntityManager |
| EntityManager | JPA interface for database operations |
| JPQL | Object-oriented query language for JPA |
| @MessageDriven | Message-driven bean for async processing |