后端Spring Boot+前端Android交互+MySQL增删查改(Java+Kotlin实现)
1 前言&概述
这篇文章是基于这篇文章的更新,主要是更新了一些技术栈以及开发工具的版本,还有修复了一些Bug。
本文是SpringBoot
+Android
+MySQL
的增删查改的简单实现,用到的技术包括Jackson
、OkHttp
、bouncycastle
、Spring Data JPA
。
2 环境
Android 4.1.2
IDEA 2020.3.1
Spring Boot 2.4.2
MySQL 8.0.23
OpenJDK 11
环境准备就略过了,需要的可以参考这里。
3 后端
3.1 新建项目
依赖:
3.2 项目结构
3.3 实体类
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private String password;
}
基本的Lombok
注解+JPA
中的两个注解:
@Id
:标识主键@GeneratedValue
:主键生成策略,包括四个
主键生成策略如下:
GenerationType.TABLE
:使用一个特定的数据库表格来保存主键,不依赖外部环境和数据库的具体实现,但是不能充分利用数据库特性,一般不会优先使用,且一般配合@TableGenerator
使用GenerationType.SEQUENCE
:一些数据库不支持主键自增(如Oracle
),这时就可以使用SEQUENCE
,只有部分(Oracle
/DB2
/PostgreSQL
)支持序列对象,一般不用于其他数据库GenerationType.IDENTITY
:一般意义上的主键自增长,插入数据时自动给主键复制,比如MySQL
中的auto_increment
GenerationType.AUTO
:主键生成策略交给持久化引擎,持久化引擎会根据数据库在以上三种主键策略中选择其中一种,这是JPA
默认的主键生成策略
3.4 持久层
继承CrudRepository<T,ID>
,T
为实体类,ID
为主键类型:
@Repository
public interface UserRepository extends CrudRepository<User,Integer> {
boolean existsByName(String name);
User findByNameAndPassword(String name,String password);
}
一个需要注意的点是CrudRepository<T,ID>
继承了Repository<T,ID>
,而后者有一个叫查询方法
的特性,就是说能根据一些方法中指定的关键字去生成对应的SQL
,比如第一个方法existsByName
,就根据name
判断用户是否存在,参数为一个String name
,返回boolean
,具体的关键字以及例子参考如下:
3.5 业务层
@Transactional
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserService {
private final UserRepository repository;
public boolean exists(User user){
return repository.existsByName(user.getName());
}
public User findByNameAndPassword(User user){
return repository.findByNameAndPassword(user.getName(),user.getPassword());
}
public boolean insert(User user){
repository.save(user);
return true;
}
public boolean update(User user){
if(repository.findById(user.getId()).isEmpty()){
return false;
}
repository.save(user);
return true;
}
public boolean deleteById(int id){
if(!repository.existsById(id)){
return false;
}
repository.deleteById(id);
return true;
}
}
注解解释如下:
@Transactional
:@Service
:标识为业务层,实际效果等价于@Component
@RequiredArgsConstructor
:Lombok
中的一个注解,主要是为了解决如下的警告:
其他一些根据方法名就知道含义的方法就不解释了。
3.6 控制层
@RestController
@RequestMapping("/")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserController {
private final UserService service;
@PostMapping("sign/in/up")
public ResponseBody signInUp(@RequestBody User user) {
if (service.exists(user)) {
User u = service.findByNameAndPassword(user);
return new ResponseBody(u != null ? ResponseCode.SIGN_IN_SUCCESS : ResponseCode.SIGN_IN_FAILED, u != null ? u.getId() : "");
}
return new ResponseBody(service.insert(user) ? ResponseCode.SIGN_UP_SUCCESS : ResponseCode.SIGN_UP_FAILED, "");
}
@PutMapping("update")
public ResponseBody update(@RequestBody User user) {
return new ResponseBody(service.update(user) ? ResponseCode.UPDATE_SUCCESS : ResponseCode.UPDATE_FAILED, "");
}
@DeleteMapping("delete")
public ResponseBody deleteByName(@RequestParam int id) {
return new ResponseBody(service.deleteById(id) ? ResponseCode.DELETE_SUCCESS : ResponseCode.DELETE_FAILED, "");
}
@GetMapping("test")
public String test() {
return "test";
}
}
注解解释如下:
@RestController
:等价于@ResponseBody
+@Controller
,@RepsonseBody
是直接返回数据的注解(不是默认的视图名字),而@Controller
与@Service
类似,查看源码可知道都是@Component
的别名@RequestMapping
:表示该类中的方法中包含的Mapping
都以此值开头@PostMapping
/@PutMapping
/@DeleteMapping
/@GetMapping
:标识处理POST
/PUT
/Delete
/GET
请求的路径,如果类上添加了@RequestMapping
,则把路径拼接在@RequestMapping
的后面,比如这里的@GetMapping("test")
相当于/test
3.7 响应体+响应码
响应体:
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class ResponseBody {
private int code;
private Object data;
}
响应码:
public class ResponseCode {
public static final int SIGN_IN_SUCCESS = 2000;
public static final int SIGN_UP_SUCCESS = 2001;
public static final int UPDATE_SUCCESS = 2002;
public static final int DELETE_SUCCESS = 2003;
public static final int SIGN_IN_FAILED = 3000;
public static final int SIGN_UP_FAILED = 3001;
public static final int UPDATE_FAILED = 3002;
public static final int DELETE_FAILED = 3003;
}
3.8 配置文件
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/userinfo
jpa:
open-in-view: false
hibernate:
ddl-auto: update
数据库名字以及用户名密码请根据自己需要修改,open-in-view
这个选项在JPA
默认为true
,设置为false
是为了抑制一个警告,开启它的含义是在事务外也可以访问懒加载的数据,这样可能会引起手动数据源切换失败的问题,因此设置为false
。
ddl-auto: update
表示更新数据表,原有数据保留,而且能在没有创建表的情况下自动创建表。该参数一共有5个设置选项:update
、create
、create-drop
、validate
、none
,具体区别可以查看这里。
3.9 测试
运行后可以访问本地的localhost:8080/test
会看到如下页面:
这样就没问题了,剩下的需要配合Android
端测试。
4 Android
端
4.1 新建项目
Android Q+Java
。
4.2 依赖+权限
依赖:
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.1'
implementation 'org.bouncycastle:bcprov-jdk15to18:1.68'
implementation "org.projectlombok:lombok:1.18.16"
annotationProcessor 'org.projectlombok:lombok:1.18.16'
权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--<application>中-->
android:usesCleartextTraffic="true"
开启viewBinding
:
buildFeatures{
viewBinding = true
}
4.3 项目结构
4.4 实体类
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class User {
private int id;
private String name;
private String password;
public User(String name, String password) {
this.name = name;
this.password = password;
}
}
4.5 工具类
public class Utils {
private static final Keccak.Digest512 digest512 = new Keccak.Digest512();
public static String encrypt(String origin) {
return new String(Hex.encode(digest512.digest(origin.getBytes(StandardCharsets.UTF_8))));
}
public static String getResponseMessage(int code) {
String message = "";
switch (code) {
case ResponseCode.SIGN_IN_SUCCESS:
message = "登录成功";
break;
case ResponseCode.SIGN_UP_SUCCESS:
message = "注册成功";
break;
case ResponseCode.SIGN_IN_FAILED:
message = "用户名或密码错误";
break;
case ResponseCode.SIGN_UP_FAILED:
message = "注册失败";
break;
case ResponseCode.DELETE_FAILED:
message = "删除失败";
break;
case ResponseCode.DELETE_SUCCESS:
message = "删除成功,自动退出";
break;
case ResponseCode.UPDATE_SUCCESS:
message = "更新成功";
break;
case ResponseCode.UPDATE_FAILED:
message = "更新失败";
break;
case ResponseCode.EMPTY_RESPONSE:
message = "响应体为空";
break;
case ResponseCode.SERVER_ERROR:
message = "服务器错误";
break;
case ResponseCode.JSON_SERIALIZATION:
message = "JSON序列化错误";
break;
case ResponseCode.EXIT_SUCCESS:
message = "退出成功";
break;
case ResponseCode.REQUEST_FAILED:
message = "请求发送失败";
break;
case ResponseCode.UNCHANGED_INFORMATION:
message = "未修改信息";
break;
}
return message;
}
public static void showMessage(Context context, Message message) {
Toast.makeText(context, getResponseMessage(message.what), Toast.LENGTH_SHORT).show();
}
}
工具类有三个方法,分别是:
- 加密:将密码进行
SHA3-512
加密,加密后的密码再发送到后端 - 获取对应信息:根据
Message
获取对应的提示信息 - 展示信息:利用
Toast
展示信息
4.6 响应体+响应码
响应体:
@NoArgsConstructor
@Setter
@Getter
public class RestResponse {
private int code;
private Object data;
}
响应码:
public class ResponseCode {
public static final int SIGN_IN_SUCCESS = 2000;
public static final int SIGN_UP_SUCCESS = 2001;
public static final int UPDATE_SUCCESS = 2002;
public static final int DELETE_SUCCESS = 2003;
public static final int SIGN_IN_FAILED = 3000;
public static final int SIGN_UP_FAILED = 3001;
public static final int UPDATE_FAILED = 3002;
public static final int DELETE_FAILED = 3003;
public static final int EMPTY_RESPONSE = 4000;
public static final int SERVER_ERROR = 4001;
public static final int REQUEST_FAILED = 4002;
public static final int JSON_SERIALIZATION = 4003;
public static final int EXIT_SUCCESS = 4004;
public static final int UNCHANGED_INFORMATION = 4005;
}
4.7 请求URL
常量
public class NetworkSettings {
private static final String HOST = "192.168.1.8";
private static final String PORT = "8080";
public static final String SIGN_IN_UP = "http://"+ HOST +":"+PORT + "/sign/in/up";
public static final String UPDATE = "http://"+ HOST +":"+PORT + "/update";
public static final String DELETE = "http://"+ HOST +":"+PORT + "/delete";
}
4.8 MainActivity
上一部分代码吧,剩下的大部分类似,看源码链接即可。
public void signInUp(View view) {
try {
String name = binding.name.getText().toString();
//SHA3-512加密
String password = Utils.encrypt(binding.password.getText().toString());
//构造OkHttp请求Request
Request request = new Request.Builder().url(NetworkSettings.SIGN_IN_UP).post(
//请求体类型为application/json;charset=utf-8,利用了Jackson序列化为JSON
RequestBody.create(mapper.writeValueAsString(new User(name, password)), mediaType)
).build();
//异步POST操作,传入一个Callback回调
client.newCall(request).enqueue(new Callback() {
//若失败
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
//请求失败信息
message.what = ResponseCode.REQUEST_FAILED;
//展示对应信息,注意不能直接使用Toast.make(getApplicationContext(),"message",Toast.LENGTH_SHORT).show()
//因为不是同一个线程,需要使用Handler提交,也就是post()方法,参数为一个线程
handler.post(()->Utils.showMessage(getApplicationContext(),message));
e.printStackTrace();
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
//如果成功
if (response.isSuccessful()) {
//获取请求体
ResponseBody body = response.body();
//如果响应体不为空
if (body != null) {
//反序列化为响应体,包含了一个响应码以及数据字段
RestResponse restResponse = mapper.readValue(body.string(), RestResponse.class);
//设置Message
message.what = restResponse.getCode();
//如果登录成功
if(message.what == ResponseCode.SIGN_IN_SUCCESS){
handler.post(()->{
//存储用户id
signInId = (int)restResponse.getData();
//更新UI
binding.update.setVisibility(View.VISIBLE);
binding.delete.setVisibility(View.VISIBLE);
binding.signInUp.setText("退出");
binding.signInUp.setOnClickListener(v->signOut(false));
//保存旧用户名以及旧密码在更新的时候使用
oldName = binding.name.getText().toString();
oldPassword = binding.password.getText().toString();
});
}
} else {
//空响应体
message.what = ResponseCode.EMPTY_RESPONSE;
Log.e("RESPONSE_BODY_EMPTY", response.message());
}
} else {
//服务器错误
message.what = ResponseCode.SERVER_ERROR;
Log.e("SERVER_ERROR", response.message());
}
//根据Message提示对应信息
handler.post(()->Utils.showMessage(getApplicationContext(),message));
}
});
} catch (JsonProcessingException e) {
message.what = ResponseCode.JSON_SERIALIZATION;
Utils.showMessage(getApplicationContext(),message);
e.printStackTrace();
}
}
这部分是登录注册的代码,还有更新用户信息以及删除用户的代码,大部分类似。
5 测试
6 注意事项
如果出现了问题某些功能不能正常实现可以参考此处的一些注意事项以及解决方案。
7 源码
提供了Java
+Kotlin
两种实现:
8 参考链接
2、spring boot jpa学习:2.DAO和Service的自增id、删、查、改操作
如果觉得文章好看,欢迎点赞。
同时欢迎关注微信公众号:氷泠之路。
后端Spring Boot+前端Android交互+MySQL增删查改(Java+Kotlin实现)的更多相关文章
- 后端Spring Boot+前端Android交互+MySQL增删查改
2021.1.27 更新 已更新新版本博客,更新内容很多,因此新开了一篇博客,戳这里. 1 概述 使用spring boot作为后端框架与Android端配合mysql进行基本的交互,包含了最基本的增 ...
- php mysql增删查改
php mysql增删查改代码段 $conn=mysql_connect('localhost','root','root'); //连接数据库代码 mysql_query("set na ...
- mysql 增删查改
非关系型数据库关系型数据库Oracle mysql sqlserver db2 Postgresql Sqlite access sqlserver 微软db2 ibm================ ...
- node.js+mysql增删查改
数据库和表: -- -- 数据库: `test` -- -- -------------------------------------------------------- -- -- 表的结构 ` ...
- MySQL 增删查改 必知必会
MySQL 数据库中的基础操作 3.表的修改 对表的表名.字段.字段类型.字段长度.约束等进行修改. 3.1 表的名称修改 -- 语法: ALTER TABLE 库名.表名 RENAME TO 新表名 ...
- python操作mysql增删查改
# coding=utf-8 ''' python操作mysql,需安装MySQLdb驱动 安装MySQLdb,请访问 http://sourceforge.net/projects/mysql-py ...
- 靠谱好用,ANDROID SQLITE 增删查改
布局文件main实现简单的功能: 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayo ...
- MySQL增删查改语句(入门)
目录 create alter: insert delete update select 数据库定义语句: create:创建数据库及表对象 drop:删除数据库及表对象 alter:修改数据库及表对 ...
- mysql增删查改练习
建表 班级表 create table class( cid int auto_increment unique, caption varchar(32) not null default '' )c ...
随机推荐
- 实现TensorRT-7.0插件自由!(如果不踩坑使用TensorRT插件功能)
本系列为新TensorRT的第一篇,为什么叫新,因为之前已经写了两篇关于TensorRT的文章,是关于TensorRT-5.0版本的.好久没写关于TensorRT的文章了,所幸就以新来开头吧~ 接下来 ...
- 【Python】set 与 list ——如何对列表进行去重?
在Python中,形如 {1,2,3,4,5} 这样的数据类型叫做"集合",外形酷似列表list [1,2,3,4,5] 但是集合与列表有很多区别,具体表现在以下几方面: List ...
- Redis数据结构和对象三
1.Redis 对象系统 Redis用到的所有主要数据结构,简单动态字符串(SDS).双端链表.字典.压缩列表.整数集合.跳跃表. Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些 ...
- sqlyog如何增删改查?
转: sqlyog如何增删改查? 下面是一道完整的 sqlyog 增删改查的练习, 顺着做下去,可以迅速掌握. 1. 创建部门表dept,并插入数据: 2. 创建emp员工表,并插入数据: sql 代 ...
- entitybuilder--一个简单的业务通用框架
关于业务通用框架的思考 业务系统是千差万别的,例如,保存.更新和删除订单,或者保存订单和保存客户,走的根本不是一个流程.但是,它们还是有共同点,它们的流程大致可以分成下面的几个部分: 拿到增删改等操作 ...
- 653. 两数之和 IV - 输入 BST + HashSet
653. 两数之和 IV - 输入 BST 题目描述 题解分析 最简单的方法就是遍历整棵树,找出所有可能的组合,判断是否存在和为 kk 的一对节点.现在在此基础上做一些改进. 如果存在两个元素之和为 ...
- HDOJ-6666(简单题+模拟题)
quailty and ccpc hdoj-6666 题目很简单,按照题目的意思模拟就行了,排序. #include<iostream> #include<cstdio> #i ...
- redis基础:redis下载安装与配置,redis数据类型使用,redis常用指令,jedis使用,RDB和AOF持久化
知识点梳理 课堂讲义 课程计划 1. REDIS 入 门 (了解) (操作) 2. 数据类型 (重点) (操作) (理解) 3. 常用指令 (操作) 4. Jedis (重点) (操作) ...
- Java I/O流 01
文件IO·异常 和 File类 异常的概述和分类 * A:异常的概述 * 异常就是Java程序在运行过程中出现的错误 * B:异常的分类 * 用过API查看Throwable * Error * 服务 ...
- IDEA中便捷内存数据库H2的最简使用方式
在IDEA中有时候为了练习,需要使用到数据库,但如果自己工作或开发机子上本来没有安装数据库,也没有可用的远程数据库时,我们可以直接在IDEA环境上使用便捷式的内存数据库H2,关于H2更多知识就自己去找 ...