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=TRACE
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;
}
}
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
经验文字,请点击:


