杰瑞科技汇

java service dao

Of course! Let's break down the classic Service-DAO architecture pattern in Java. This is a foundational design pattern for building robust, maintainable, and testable applications, especially in enterprise environments.

java service dao-图1
(图片来源网络,侵删)

The Big Picture: Why Use This Pattern?

Imagine you're building an application that manages users. You need to:

  1. Save a new user to the database.
  2. Find a user by their ID.
  3. Update a user's email address.
  4. Delete a user.

Without a pattern, you might put all the database logic directly in your code that handles web requests (like a Spring MVC controller). This is a bad idea because:

  • Tight Coupling: Your web layer becomes tightly linked to your database. If you decide to switch from a MySQL database to a MongoDB database, you have to change your web layer.
  • Hard to Test: Testing the web layer requires a real database connection. This is slow and complex.
  • Code Reuse: If another part of your application (like a scheduled job) also needs to find a user, you have to duplicate the database logic.
  • Violation of Single Responsibility Principle: A class should do one thing well. A controller should handle HTTP requests, not manage database connections and SQL queries.

The Service-DAO pattern solves these problems by creating a clear separation of concerns.


The Three Layers

Let's visualize the flow of a request through the three main layers:

java service dao-图2
(图片来源网络,侵删)

Client (e.g., Web Browser) -> Controller -> Service -> DAO -> Database

Here's what each layer does:

Controller Layer (The "Front Desk")

  • Responsibility: Handles incoming HTTP requests from a client (like a web browser or another API).
  • Tasks:
    • Parse request parameters (e.g., from a URL or JSON body).
    • Call the appropriate Service method.
    • Receive the result from the Service.
    • Format the result into an HTTP response (e.g., JSON, XML, or an HTTP status code like 200 OK or 404 Not Found).
  • Key Point: The Controller knows nothing about the database. It only knows about the Service layer.

Service Layer (The "Business Logic")

  • Responsibility: Contains the core business logic of the application. This is the "brain" of your application.
  • Tasks:
    • Orchestrate operations by calling one or more DAOs.
    • Implement business rules and validations.
    • Manage transactions (e.g., ensure that if two database operations must succeed, they either both succeed or both fail).
    • Perform calculations or data transformations.
  • Example: A UserService might have a registerUser method that first checks if the username is already taken (using a UserDAO), then saves the new user (using UserDAO again), and then sends a welcome email. The controller just says "register this user," and the service handles the "how."

DAO Layer (The "Data Accessor")

  • Responsibility: Provides an abstract interface to the database. Its only job is to perform CRUD (Create, Read, Update, Delete) operations.
  • Tasks:
    • Execute SQL queries or use an ORM (like Hibernate/JPA) to interact with the database.
    • Map database results (like ResultSet objects) to Java objects (like User entities).
    • Handle low-level database exceptions.
  • Key Point: The DAO is completely ignorant of the business logic. It doesn't know why you're saving a user, only how to save it.

Code Example: A User Management System

Let's see this in action with a simple Spring Boot application.

The Model (Entity)

This is the plain Java object that represents our data.

java service dao-图3
(图片来源网络,侵删)

User.java

package com.example.demo.model;
// JPA annotation to mark this as a database entity
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity // Marks this class as a JPA entity (a table in the database)
public class User {
    @Id // Marks this field as the primary key
    @GeneratedValue(strategy = GenerationType.IDENTITY) // Auto-incrementing ID
    private Long id;
    private String username;
    private String email;
    // Constructors, Getters, and Setters (omitted for brevity)
    // It's good practice to have a no-arg constructor for JPA
}

The DAO Layer

First, the interface that defines the contract.

UserDao.java

package com.example.demo.dao;
import com.example.demo.model.User;
import java.util.Optional;
// This interface defines the data access operations for the User entity.
public interface UserDao {
    // Find a user by their ID
    Optional<User> findById(Long id);
    // Find a user by their username
    Optional<User> findByUsername(String username);
    // Save a new user or update an existing one
    void save(User user);
    // Delete a user by their ID
    void deleteById(Long id);
}

Now, the implementation. In a real Spring app, this would likely use Spring Data JPA, which automatically generates the implementation for you. But to show the pattern, here's a manual implementation.

UserDaoImpl.java (This is often simplified with Spring Data)

package com.example.demo.dao;
import com.example.demo.model.User;
import org.springframework.stereotype.Repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.Optional;
import java.util.List;
@Repository // Stereotype annotation for a DAO component
public class UserDaoImpl implements UserDao {
    @PersistenceContext // Injects the JPA EntityManager
    private EntityManager entityManager;
    @Override
    public Optional<User> findById(Long id) {
        User user = entityManager.find(User.class, id);
        return Optional.ofNullable(user);
    }
    @Override
    public Optional<User> findByUsername(String username) {
        // In a real app, you'd use a JPQL query or a CriteriaQuery
        List<User> users = entityManager.createQuery(
            "SELECT u FROM User u WHERE u.username = :username", User.class)
            .setParameter("username", username)
            .getResultList();
        return users.isEmpty() ? Optional.empty() : Optional.of(users.get(0));
    }
    @Override
    public void save(User user) {
        if (user.getId() == null) {
            entityManager.persist(user); // New entity
        } else {
            entityManager.merge(user); // Existing entity
        }
    }
    @Override
    public void deleteById(Long id) {
        User user = entityManager.find(User.class, id);
        if (user != null) {
            entityManager.remove(user);
        }
    }
}

The Service Layer

First, the interface.

UserService.java

package com.example.demo.service;
import com.example.demo.model.User;
public interface UserService {
    User createUser(String username, String email);
    Optional<User> getUserById(Long id);
    // ... other methods like updateUser, deleteUser
}

Now, the implementation, which contains the business logic.

UserServiceImpl.java

package com.example.demo.service;
import com.example.demo.dao.UserDao;
import com.example.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Service // Stereotype annotation for a Service component
public class UserServiceImpl implements UserService {
    // The service depends on the DAO to interact with the database
    private final UserDao userDao;
    // Use constructor injection (best practice)
    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    @Transactional // This ensures the method runs in a transaction
    public User createUser(String username, String email) {
        // Business Rule: Username must not be empty
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("Username cannot be empty.");
        }
        // Business Rule: Username must be unique
        if (userDao.findByUsername(username).isPresent()) {
            throw new IllegalStateException("Username '" + username + "' is already taken.");
        }
        // Create the new user object
        User newUser = new User();
        newUser.setUsername(username);
        newUser.setEmail(email);
        // Delegate the saving to the DAO layer
        userDao.save(newUser);
        return newUser;
    }
    @Override
    public Optional<User> getUserById(Long id) {
        // Simply delegate to the DAO. No business logic needed here.
        return userDao.findById(id);
    }
}

The Controller Layer

This exposes our service functionality via a REST API.

UserController.java

package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController // Combines @Controller and @ResponseBody
@RequestMapping("/api/users") // Base path for all endpoints in this controller
public class UserController {
    private final UserService userService;
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }
    // Endpoint to create a new user
    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        try {
            // The controller just calls the service and returns the result
            User createdUser = userService.createUser(user.getUsername(), user.getEmail());
            return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
        } catch (IllegalArgumentException | IllegalStateException e) {
            // Handle business logic errors (e.g., duplicate username)
            return new ResponseEntity<>(null, HttpStatus.CONFLICT); // 409 Conflict
        } catch (Exception e) {
            // Handle other unexpected errors
            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    // Endpoint to get a user by ID
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userService.getUserById(id)
                .map(user -> new ResponseEntity<>(user, HttpStatus.OK)) // 200 OK
                .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); // 404 Not Found
    }
}

Summary of Benefits

Layer Responsibility Benefits
Controller HTTP Request/Response Handling - Decouples presentation logic from business logic.
- Easy to test without a web server.
Service Business Logic & Orchestration - Single Responsibility: Focuses on business rules.
- Testability: Business logic can be unit-tested by mocking DAOs.
- Reusability: Business logic can be used by different clients (web, mobile, batch jobs).
DAO Data Persistence - Abstraction: The rest of the app is unaware of the database type (SQL, NoSQL, file system).
- Testability: You can test the Service layer with a mock DAO that doesn't need a real DB.
- Maintainability: Switching databases or data sources becomes much easier.

Modern Variations & Frameworks

  • Spring Data JPA / Spring Data MongoDB: These frameworks drastically simplify the DAO layer. You typically only need to define an interface (like public interface UserRepository extends JpaRepository<User, Long> {}), and Spring provides the implementation automatically. The pattern still holds, but the DAO implementation is handled by the framework.
  • Repository Pattern: This is often used as a synonym for DAO, but it can be a more generic data access layer that might not map 1-to-1 with a database table (e.g., it could query multiple tables).
  • DTO (Data Transfer Object): A common addition to this pattern is to use DTOs to pass data between layers. A User entity might have a password field, but you wouldn't want to send that to a client. A UserDto would contain only id, username, and email. The Service layer would convert the User entity to a UserDto before returning it to the Controller. This adds another layer of security and flexibility.
分享:
扫描分享到社交APP
上一篇
下一篇