DEV Community

Cyrus Tse
Cyrus Tse

Posted on

Spring Boot 4 入門教學 - Part 7 (完整 CRUD 專案實作)

第七章:完整的 CRUD 專案實作

7.1 專案結構總覽

在這一章節中,我們將整合前幾章學習的所有知識,創建一個完整的 Spring Boot CRUD 專案。這個專案將包含:

  • User 實體(對應資料庫表)
  • Repository 層(資料存取)
  • Service 層(業務邏輯)
  • Controller 層(REST API)
  • DTO 類(資料傳輸對象)
  • 異常處理
  • 統一響應格式

專案結構如下:

src/main/java/com/example/myfirstapp/
├── MyFirstAppApplication.java          # 主應用程式類
├── config/
│   ├── AppConfig.java                  # 應用配置
│   ├── GlobalExceptionHandler.java     # 全局異常處理
│   └── ResourceNotFoundException.java  # 自定義異常
├── controller/
│   └── UserController.java             # REST 控制器
├── dto/
│   ├── ApiResponse.java                # 統一響應格式
│   ├── CreateUserRequest.java          # 創建用戶請求
│   └── UserResponse.java               # 用戶響應
├── entity/
│   └── User.java                       # 用戶實體
├── repository/
│   └── UserRepository.java             # 資料存取層
└── service/
    └── UserService.java                # 業務邏輯層

src/main/resources/
├── application.properties              # 應用配置
└── application.yml                     # 應用配置(YAML格式)
Enter fullscreen mode Exit fullscreen mode

7.2 完整的程式碼清單

以下是完整的專案程式碼,你可以直接複製使用。

主應用程式類:

package com.example.myfirstapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyFirstAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyFirstAppApplication.class, args);
    }
}
Enter fullscreen mode Exit fullscreen mode

User 實體類:

package com.example.myfirstapp.entity;

import java.time.LocalDateTime;

public class User {
    private Long id;
    private String username;
    private String email;
    private String password;
    private String fullName;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    private Boolean active;

    // 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; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public String getFullName() { return fullName; }
    public void setFullName(String fullName) { this.fullName = fullName; }
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
    public LocalDateTime getUpdatedAt() { return updatedAt; }
    public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
    public Boolean isActive() { return active; }
    public void setActive(Boolean active) { this.active = active; }
}
Enter fullscreen mode Exit fullscreen mode

Repository 類:

package com.example.myfirstapp.repository;

import com.example.myfirstapp.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public class UserRepository {

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public UserRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public List<User> findAll() {
        String sql = "SELECT id, username, email, password, full_name, " +
                     "created_at, updated_at, active FROM users ORDER BY id";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
    }

    public Optional<User> findById(Long id) {
        String sql = "SELECT id, username, email, password, full_name, " +
                     "created_at, updated_at, active FROM users WHERE id = ?";
        try {
            User user = jdbcTemplate.queryForObject(sql,
                new BeanPropertyRowMapper<>(User.class), id);
            return Optional.ofNullable(user);
        } catch (Exception e) {
            return Optional.empty();
        }
    }

    public User save(User user) {
        if (user.getId() == null) {
            String sql = "INSERT INTO users (username, email, password, full_name, " +
                         "created_at, updated_at, active) VALUES (?, ?, ?, ?, ?, ?, ?)";
            java.time.LocalDateTime now = java.time.LocalDateTime.now();
            jdbcTemplate.update(sql,
                user.getUsername(), user.getEmail(), user.getPassword(),
                user.getFullName(), now, now,
                user.isActive() != null ? user.isActive() : true);

            String currSql = "SELECT currval(pg_get_serial_sequence('users', 'id'))";
            Long id = jdbcTemplate.queryForObject(currSql, Long.class);
            user.setId(id);
            user.setCreatedAt(now);
            user.setUpdatedAt(now);
        } else {
            String sql = "UPDATE users SET username = ?, email = ?, password = ?, " +
                         "full_name = ?, updated_at = ?, active = ? WHERE id = ?";
            jdbcTemplate.update(sql,
                user.getUsername(), user.getEmail(), user.getPassword(),
                user.getFullName(), java.time.LocalDateTime.now(),
                user.isActive(), user.getId());
            user.setUpdatedAt(java.time.LocalDateTime.now());
        }
        return user;
    }

    public void deleteById(Long id) {
        String sql = "DELETE FROM users WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }

    public boolean existsByUsername(String username) {
        String sql = "SELECT COUNT(*) FROM users WHERE username = ?";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class, username);
        return count != null && count > 0;
    }

    public boolean existsByEmail(String email) {
        String sql = "SELECT COUNT(*) FROM users WHERE email = ?";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class, email);
        return count != null && count > 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

Service 類:

package com.example.myfirstapp.service;

import com.example.myfirstapp.config.ResourceNotFoundException;
import com.example.myfirstapp.entity.User;
import com.example.myfirstapp.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> findAll() {
        return userRepository.findAll();
    }

    public User findById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("User", id));
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void deleteById(Long id) {
        if (!userRepository.findById(id).isPresent()) {
            throw new ResourceNotFoundException("User", id);
        }
        userRepository.deleteById(id);
    }

    public boolean existsByUsername(String username) {
        return userRepository.existsByUsername(username);
    }

    public boolean existsByEmail(String email) {
        return userRepository.existsByEmail(email);
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)