我们相信:世界是美好的,你是我也是。 来玩一下解压小游戏吧!

SpringBoot和数据库交互的技术规范叫做JPA: Java Persistence API,具体实现这一思想的框架叫做Hibernate。本文以user表为例,演示对数据库的增删改查操作。值的一提的是:mybatis是另外的数据库交互框架,和本文无关。

苏南大叔:SpringBoot开发,通过JPA如何做数据库增删改查? - springboot-jpa
SpringBoot开发,通过JPA如何做数据库增删改查?(图3-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。测试环境:win10openjdk@23.0.2IntelliJ IDEA 2024.3.4.1maven@3.3.2spring boot@2.5.4java@17mysql@5.7.26

前文回顾

前文中,通过对user表的数据查询,实现了用户登陆的功能。使用的代码包括model(entity)+controller。参考文章:

在此基础上,通过session来固化上面的查询结果,作为当前用户的身份凭证:

本文中,在原有的entitycontroller的概念基础上,增加了repositoryservice的概念。那么,如何理解这四种概念的分工呢?

概念区分

Spring Boot中,EntityControllerRepositoryService四种组件各自扮演不同的角色,共同构建【微服务】架构:

  • Entity:定义数据库模型,代表现实世界的实体,如订单、商品等,使用@Entity注解。相当于把数据表用代码的方式再解释一遍。
  • Controller:负责业务逻辑,处理HTTP请求,如用户登录、商品详情等。使用@RestController@Controller注解。是直接和用户打交道的部分,对外提供地址访问的功能。
  • Repository:数据库操作的接口,执行增删改查操作。使用@Repository进行注解,值得注意的是:一部分findById()之类的操作是内置的,并不需要显式的写出来。
  • Service:业务逻辑和服务实现,处理事务,如服务工厂。使用@Service,负责业务逻辑实现,不直接与数据库交互。或者说是在Repository基础上的再包装一些业务逻辑。

苏南大叔:SpringBoot开发,通过JPA如何做数据库增删改查? - 代码结构
SpringBoot开发,通过JPA如何做数据库增删改查?(图3-2)

数据表user

CREATE TABLE user (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL
);
INSERT INTO user (name, password) VALUES ('user1', 'password1');
INSERT INTO user (name, password) VALUES ('user2', 'password2');
SQL

JPA依赖及配置

pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
XML

然后就执行mvn install命令。

resources/application.properties新增配置:

spring.datasource.url=jdbc:mysql://localhost:3306/boot
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
Ini

entity及controller

src/main/java/com/example/demo/model/user.java:

package com.example.demo.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String password;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
Java

src/main/java/com/example/demo/controller/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.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUser(id, user);
    }
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}
Java

在这个例子里面,RestFul的思想,表现的淋漓尽致啊。参考文章:

repository及service

src\main\java\com\example\demo\repository\UserRepository.java:

package com.example.demo.repository;
import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findByName(String name);
}
Java

对于这个文件里面的方法,可以看到。目前仅有一个存在,但是事实上,有很多方法隐藏了起来。参考文献:

src\main\java\com\example\demo\service\UserService.java

package com.example.demo.service;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
    public User getUserById(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("ID不能为空");
        }
        return userRepository.findById(id).orElse(null);
    }
    public User createUser(User user) {
        if (user == null || user.getName() == null || user.getName().trim().isEmpty() ||
            user.getPassword() == null || user.getPassword().trim().isEmpty()) {
            throw new IllegalArgumentException("用户名和密码不能为空");
        }
        if (userRepository.findByName(user.getName()) != null) {
            throw new RuntimeException("User with the same name already exists");
        }
        return userRepository.save(user);
    }
    public User updateUser(Long id, User user) {
        if (id == null || user == null || user.getName() == null || user.getName().trim().isEmpty() ||
            user.getPassword() == null || user.getPassword().trim().isEmpty()) {
            throw new IllegalArgumentException("ID、用户名和密码不能为空");
        }
        User existingUser = userRepository.findById(id).orElse(null);
        if (existingUser != null) {
            User userWithSameName = userRepository.findByName(user.getName());
            if (userWithSameName != null && !userWithSameName.getId().equals(id)) {
                throw new RuntimeException("User with the same name already exists");
            }
            existingUser.setName(user.getName());
            existingUser.setPassword(user.getPassword());
            return userRepository.save(existingUser);
        }
        return null;
    }
    public void deleteUser(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("ID不能为空");
        }
        if (userRepository.count() <= 1) {
            throw new RuntimeException("Cannot delete the last user");
        }
        userRepository.deleteById(id);
    }
}
Java

Repository基础上的在增加外置业务逻辑就是Service

template

这个模版是在上次登陆成功页面的基础上做的,所以,要访问的是定义在LoginController里面的/success页面。

苏南大叔:SpringBoot开发,通过JPA如何做数据库增删改查? - crud
SpringBoot开发,通过JPA如何做数据库增删改查?(图3-3)

src\main\resources\templates\success.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Success</title>
</head>
<body>
    <h1>Login Successful</h1>
    <p>Welcome, <span th:text="${session.user}"></span>!</p>
    <form th:action="@{/logout}" method="get">
        <button type="submit">Logout</button>
    </form>
    <h2>User Operations</h2>
    <form id="userForm">
        <input type="text" id="userName" placeholder="Name">
        <input type="password" id="userPassword" placeholder="Password">
        <button type="button" onclick="createUser()">Create User</button>
    </form>
    <div id="userList"></div>
    <script>
        function createUser() {
            const name = document.getElementById('userName').value;
            const password = document.getElementById('userPassword').value;
            fetch('/users', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ name, password })
            })
            .then(response => response.json())
            .then(data => {
                console.log('User created:', data);
                loadUsers();
            });
        }
        function deleteUser(id) {
            if (confirm("Are you sure you want to delete this user?")) {
                fetch(`/users/${id}`, {
                    method: 'DELETE'
                })
                .then(() => {
                    console.log('User deleted');
                    loadUsers();
                });
            }
        }
        function modifyUser(id) {
            const name = prompt("Enter new name:");
            const password = prompt("Enter new password:");
            if (name && password) {
                fetch(`/users/${id}`, {
                    method: 'PUT',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ name, password })
                })
                .then(response => response.json())
                .then(data => {
                    console.log('User modified:', data);
                    loadUsers();
                });
            }
        }
        function loadUsers() {
            fetch('/users')
                .then(response => response.json())
                .then(data => {
                    const userList = document.getElementById('userList');
                    userList.innerHTML = '';
                    data.forEach(user => {
                        userList.innerHTML += `<p>${user.name} (${user.password}) <button onclick="deleteUser(${user.id})">Delete</button> <button onclick="modifyUser(${user.id})">Modify</button></p>`;
                    });
                });
        }
        document.addEventListener('DOMContentLoaded', loadUsers);
    </script>
</body>
</html>
HTML

结语

遗留的比较大的问题是:这些接口的调用是在登陆成功的/success,但是但就接口本身来说,它并没有做权限验证。那么在后续的文章里面,再讨论这个RestFul接口的权限验证问题。

更多苏南大叔的java经验文字,请点击:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   java