SpringBoot开发,通过JPA如何做数据库增删改查?
发布于 作者:苏南大叔 来源:程序如此灵动~
SpringBoot和数据库交互的技术规范叫做JPA: Java Persistence API,具体实现这一思想的框架叫做Hibernate。本文以user表为例,演示对数据库的增删改查操作。值的一提的是:mybatis是另外的数据库交互框架,和本文无关。

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。测试环境:win10,openjdk@23.0.2,IntelliJ IDEA 2024.3.4.1,maven@3.3.2,spring boot@2.5.4,java@17,mysql@5.7.26。
前文回顾
前文中,通过对user表的数据查询,实现了用户登陆的功能。使用的代码包括model(entity)+controller。参考文章:
在此基础上,通过session来固化上面的查询结果,作为当前用户的身份凭证:
本文中,在原有的entity和controller的概念基础上,增加了repository和service的概念。那么,如何理解这四种概念的分工呢?
概念区分
在Spring Boot中,Entity、Controller、Repository和Service四种组件各自扮演不同的角色,共同构建【微服务】架构:
Entity:定义数据库模型,代表现实世界的实体,如订单、商品等,使用@Entity注解。相当于把数据表用代码的方式再解释一遍。Controller:负责业务逻辑,处理HTTP请求,如用户登录、商品详情等。使用@RestController或@Controller注解。是直接和用户打交道的部分,对外提供地址访问的功能。Repository:数据库操作的接口,执行增删改查操作。使用@Repository进行注解,值得注意的是:一部分findById()之类的操作是内置的,并不需要显式的写出来。Service:业务逻辑和服务实现,处理事务,如服务工厂。使用@Service,负责业务逻辑实现,不直接与数据库交互。或者说是在Repository基础上的再包装一些业务逻辑。

数据表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');JPA依赖及配置
pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>然后就执行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=TRACEentity及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;
}
}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);
}
}在这个例子里面,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);
}对于这个文件里面的方法,可以看到。目前仅有一个存在,但是事实上,有很多方法隐藏了起来。参考文献:
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);
}
}在Repository基础上的在增加外置业务逻辑就是Service。
template
这个模版是在上次登陆成功页面的基础上做的,所以,要访问的是定义在LoginController里面的/success页面。

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>结语
遗留的比较大的问题是:这些接口的调用是在登陆成功的/success,但是但就接口本身来说,它并没有做权限验证。那么在后续的文章里面,再讨论这个RestFul接口的权限验证问题。
更多苏南大叔的java经验文字,请点击: