杰瑞科技汇

Java中DAO与Service层职责如何划分?

Of course! This is a fundamental concept in Java enterprise applications, especially those following a layered architecture. Let's break down what DAO and Service layers are, why they are used, and how they work together with a practical example.


The Big Picture: Layered Architecture

In a typical Java application (like a Spring Boot app), you separate your code into different layers to achieve a clean, maintainable, and testable structure. The most common layers are:

  1. Presentation Layer (Controller): Handles HTTP requests, validates input, and calls the Service layer. It's the "face" of your application.
  2. Service Layer (Business Logic): Contains the core business rules and workflows. It orchestrates operations, often by calling multiple DAOs.
  3. Data Access Layer (DAO - Data Access Object): Responsible for all communication with the database (or any persistent storage). It's the only layer that talks to the database.
  4. Domain/Entity Layer: Represents your business data (e.g., User, Product). These are simple POJOs (Plain Old Java Objects) with fields, getters, and setters.

DAO (Data Access Object) Layer

The primary responsibility of the DAO layer is to abstract and encapsulate all access to the persistence layer. This means your business logic (Service layer) doesn't need to know how data is saved or retrieved (e.g., is it JDBC, JPA, Hibernate, or a NoSQL database?).

Key Characteristics of a DAO:

  • Abstraction: It provides a simple interface for CRUD (Create, Read, Update, Delete) operations.
  • Encapsulation: It hides the complex SQL queries or ORM (Object-Relational Mapping) logic from the rest of the application.
  • Reusability: A DAO can be reused across different services that need to access the same entity.

Example: DAO for a User Entity

Let's assume we have a User entity.

User.java (Entity)

// This is a simple POJO representing our data.
public class User {
    private Long id;
    private String username;
    private String email;
    // Constructors, Getters, and Setters
    public User() {}
    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }
    // Getters and Setters...
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

UserDao.java (Interface) This defines the contract for data operations.

import java.util.Optional;
public interface UserDao {
    void save(User user); // Create
    Optional<User> findById(Long id); // Read by ID
    Optional<User> findByUsername(String username); // Read by a unique field
    void update(User user); // Update
    void deleteById(Long id); // Delete
}

UserDaoImpl.java (Implementation) This is where the actual database interaction happens. In a real-world Spring Boot app, this would use JPA (@Repository, JpaRepository) or JDBC (JdbcTemplate).

import org.springframework.stereotype.Repository;
@Repository // Stereotype annotation to mark this as a DAO component
public class UserDaoImpl implements UserDao {
    // In a real app, you would inject an EntityManager or JdbcTemplate here
    // For simplicity, we'll just simulate the behavior.
    @Override
    public void save(User user) {
        System.out.println("DAO: Saving user to the database: " + user.getUsername());
        // Logic to execute an INSERT SQL statement or JPA's save() method
    }
    @Override
    public Optional<User> findById(Long id) {
        System.out.println("DAO: Finding user with ID: " + id);
        // Logic to execute a SELECT SQL statement or JPA's findById() method
        // If user is found, return Optional.of(user); else return Optional.empty();
        return Optional.empty(); // Placeholder
    }
    @Override
    public Optional<User> findByUsername(String username) {
        System.out.println("DAO: Finding user with username: " + username);
        // Logic to find user by username
        return Optional.empty(); // Placeholder
    }
    @Override
    public void update(User user) {
        System.out.println("DAO: Updating user in the database: " + user.getId());
        // Logic to execute an UPDATE SQL statement
    }
    @Override
    public void deleteById(Long id) {
        System.out.println("DAO: Deleting user with ID: " + id);
        // Logic to execute a DELETE SQL statement
    }
}

Service Layer

The Service layer contains the business logic of your application. It orchestrates operations and makes decisions based on business rules. It uses DAOs to interact with data.

Key Characteristics of a Service:

  • Business Logic: Implements rules like "a user cannot have an email that already exists," "calculate a discount based on user loyalty," etc.
  • Orchestration: It can call multiple DAOs to accomplish a single task. For example, creating an order might involve saving an Order entity and updating the Product stock.
  • Transaction Management: It often manages transactions to ensure a series of operations either all succeed or all fail (atomicity).

Example: Service for User Operations

UserService.java (Interface)

import java.util.Optional;
public interface UserService {
    User createUser(String username, String email);
    Optional<User> getUserById(Long id);
    void updateUserEmail(Long id, String newEmail);
}

UserServiceImpl.java (Implementation) This is where the magic happens. Notice how it uses the UserDao to perform data operations and adds its own business logic.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service // Stereotype annotation to mark this as a service component
public class UserServiceImpl implements UserService {
    private final UserDao userDao;
    // Use constructor injection - it's a best practice
    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    @Transactional // Ensures this method runs in a transaction
    public User createUser(String username, String email) {
        // Business Logic: Check if username is already taken
        if (userDao.findByUsername(username).isPresent()) {
            throw new IllegalArgumentException("Username already exists!");
        }
        // Create the new user object
        User newUser = new User(username, email);
        // Delegate the saving operation to the DAO
        userDao.save(newUser);
        System.out.println("Service: User created successfully!");
        return newUser;
    }
    @Override
    public Optional<User> getUserById(Long id) {
        // Simply delegate to the DAO
        return userDao.findById(id);
    }
    @Override
    @Transactional
    public void updateUserEmail(Long id, String newEmail) {
        // Business Logic: Find the user first
        User user = userDao.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("User not found with ID: " + id));
        // Business Logic: You might add validation here, e.g., check if newEmail is valid
        user.setEmail(newEmail);
        // Delegate the update operation to the DAO
        userDao.update(user);
        System.out.println("Service: User email updated successfully!");
    }
}

How They Work Together: A Flow

Let's trace the flow when a new user is registered via a REST API.

  1. HTTP Request: A POST request is sent to /api/users with a JSON body: {"username": "john_doe", "email": "john.doe@example.com"}.

  2. Controller Layer (e.g., UserController):

    • The @RestController receives the request.
    • It deserializes the JSON into a User object or just extracts the username and email.
    • It calls the Service layer: userService.createUser("john_doe", "john.doe@example.com");
  3. Service Layer (e.g., UserServiceImpl):

    • The createUser method starts.
    • Business Logic: It calls userDao.findByUsername("john_doe") to check for duplicates.
    • The UserServiceImpl has no idea how findByUsername works. It just knows the contract defined in the UserDao interface.
    • The UserServiceImpl receives the result (e.g., Optional.empty()).
    • Since the username is available, it creates a new User object.
    • It calls userDao.save(newUser).
    • It returns the newly created User object.
  4. DAO Layer (e.g., UserDaoImpl):

    • The save method is invoked.
    • It takes the User object and translates it into a database command (e.g., an INSERT SQL statement using JDBC or entityManager.persist(user) using JPA).
    • It executes the command and returns control to the Service layer.
  5. Response: The Service layer returns the User object to the Controller, which then serializes it to JSON and sends an HTTP 201 Created response back to the client.


Modern Java (Spring Boot) - A More Common Approach

In modern Spring Boot applications, the DAO pattern is often simplified or replaced by Spring Data's JpaRepository. The concept is the same, but the boilerplate is reduced.

UserRepository.java (Replaces UserDao)

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
// JpaRepository provides all CRUD methods out-of-the-box!
// You just need to specify the Entity (User) and the ID type (Long).
public interface UserRepository extends JpaRepository<User, Long> {
    // You can also define custom query methods
    Optional<User> findByUsername(String username);
}

Your UserServiceImpl would then inject UserRepository instead of UserDao, but the usage is identical. This is the most common and recommended approach today.

Summary: DAO vs. Service

Feature DAO Layer Service Layer
Primary Responsibility Data Persistence (CRUD operations). Business Logic and Workflow Orchestration.
What it knows How to talk to the database (SQL, JDBC, JPA). Business rules, application workflows.
What it hides The complexity of database access. The implementation details of data access.
Dependencies Depends on the database driver/ORM. Depends on one or more DAOs/Repositories.
Example Methods save(), findById(), delete() createUser(), processOrder(), calculateTax()
分享:
扫描分享到社交APP
上一篇
下一篇