日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

安卓后端mysql_后端Spring Boot+前端Android交互+MySQL增删查改(Java+Kotlin实现)

發(fā)布時(shí)間:2025/3/21 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 安卓后端mysql_后端Spring Boot+前端Android交互+MySQL增删查改(Java+Kotlin实现) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1 前言&概述

這篇文章是基于這篇文章的更新,主要是更新了一些技術(shù)棧以及開發(fā)工具的版本,還有修復(fù)了一些Bug。

本文是SpringBoot+Android+MySQL的增刪查改的簡單實(shí)現(xiàn),用到的技術(shù)包括Jackson、OkHttp、bouncycastle、Spring Data JPA。

2 環(huán)境

Android 4.1.2

IDEA 2020.3.1

Spring Boot 2.4.2

MySQL 8.0.23

OpenJDK 11

環(huán)境準(zhǔn)備就略過了,需要的可以參考這里。

3 后端

3.1 新建項(xiàng)目

依賴:

3.2 項(xiàng)目結(jié)構(gòu)

3.3 實(shí)體類

@Entity

@Getter

@Setter

@NoArgsConstructor

@AllArgsConstructor

public class User {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

private Integer id;

private String name;

private String password;

}

基本的Lombok注解+JPA中的兩個(gè)注解:

@Id:標(biāo)識(shí)主鍵

@GeneratedValue:主鍵生成策略,包括四個(gè)

主鍵生成策略如下:

GenerationType.TABLE:使用一個(gè)特定的數(shù)據(jù)庫表格來保存主鍵,不依賴外部環(huán)境和數(shù)據(jù)庫的具體實(shí)現(xiàn),但是不能充分利用數(shù)據(jù)庫特性,一般不會(huì)優(yōu)先使用,且一般配合@TableGenerator使用

GenerationType.SEQUENCE:一些數(shù)據(jù)庫不支持主鍵自增(如Oracle),這時(shí)就可以使用SEQUENCE,只有部分(Oracle/DB2/PostgreSQL)支持序列對(duì)象,一般不用于其他數(shù)據(jù)庫

GenerationType.IDENTITY:一般意義上的主鍵自增長,插入數(shù)據(jù)時(shí)自動(dòng)給主鍵復(fù)制,比如MySQL中的auto_increment

GenerationType.AUTO:主鍵生成策略交給持久化引擎,持久化引擎會(huì)根據(jù)數(shù)據(jù)庫在以上三種主鍵策略中選擇其中一種,這是JPA默認(rèn)的主鍵生成策略

3.4 持久層

繼承CrudRepository,T為實(shí)體類,ID為主鍵類型:

@Repository

public interface UserRepository extends CrudRepository {

boolean existsByName(String name);

User findByNameAndPassword(String name,String password);

}

一個(gè)需要注意的點(diǎn)是CrudRepository繼承了Repository,而后者有一個(gè)叫查詢方法的特性,就是說能根據(jù)一些方法中指定的關(guān)鍵字去生成對(duì)應(yīng)的SQL,比如第一個(gè)方法existsByName,就根據(jù)name判斷用戶是否存在,參數(shù)為一個(gè)String name,返回boolean,具體的關(guān)鍵字以及例子參考如下:

3.5 業(yè)務(wù)層

@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:標(biāo)識(shí)為業(yè)務(wù)層,實(shí)際效果等價(jià)于@Component

@RequiredArgsConstructor:Lombok中的一個(gè)注解,主要是為了解決如下的警告:

其他一些根據(jù)方法名就知道含義的方法就不解釋了。

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:等價(jià)于@ResponseBody+@Controller,@RepsonseBody是直接返回?cái)?shù)據(jù)的注解(不是默認(rèn)的視圖名字),而@Controller與@Service類似,查看源碼可知道都是@Component的別名

@RequestMapping:表示該類中的方法中包含的Mapping都以此值開頭

@PostMapping/@PutMapping/@DeleteMapping/@GetMapping:標(biāo)識(shí)處理POST/PUT/Delete/GET請(qǐng)求的路徑,如果類上添加了@RequestMapping,則把路徑拼接在@RequestMapping的后面,比如這里的@GetMapping("test")相當(dāng)于/test

3.7 響應(yīng)體+響應(yīng)碼

響應(yīng)體:

@Getter

@Setter

@AllArgsConstructor

@NoArgsConstructor

public class ResponseBody {

private int code;

private Object data;

}

響應(yīng)碼:

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

數(shù)據(jù)庫名字以及用戶名密碼請(qǐng)根據(jù)自己需要修改,open-in-view這個(gè)選項(xiàng)在JPA默認(rèn)為true,設(shè)置為false是為了抑制一個(gè)警告,開啟它的含義是在事務(wù)外也可以訪問懶加載的數(shù)據(jù),這樣可能會(huì)引起手動(dòng)數(shù)據(jù)源切換失敗的問題,因此設(shè)置為false。

ddl-auto: update表示更新數(shù)據(jù)表,原有數(shù)據(jù)保留,而且能在沒有創(chuàng)建表的情況下自動(dòng)創(chuàng)建表。該參數(shù)一共有5個(gè)設(shè)置選項(xiàng):update、create、create-drop、validate、none,具體區(qū)別可以查看這里。

3.9 測(cè)試

運(yùn)行后可以訪問本地的localhost:8080/test會(huì)看到如下頁面:

這樣就沒問題了,剩下的需要配合Android端測(cè)試。

4 Android端

4.1 新建項(xiàng)目

Android Q+Java。

4.2 依賴+權(quán)限

依賴:

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'

權(quán)限:

android:usesCleartextTraffic="true"

開啟viewBinding:

buildFeatures{

viewBinding = true

}

4.3 項(xiàng)目結(jié)構(gòu)

4.4 實(shí)體類

@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 = "注冊(cè)成功";

break;

case ResponseCode.SIGN_IN_FAILED:

message = "用戶名或密碼錯(cuò)誤";

break;

case ResponseCode.SIGN_UP_FAILED:

message = "注冊(cè)失敗";

break;

case ResponseCode.DELETE_FAILED:

message = "刪除失敗";

break;

case ResponseCode.DELETE_SUCCESS:

message = "刪除成功,自動(dòng)退出";

break;

case ResponseCode.UPDATE_SUCCESS:

message = "更新成功";

break;

case ResponseCode.UPDATE_FAILED:

message = "更新失敗";

break;

case ResponseCode.EMPTY_RESPONSE:

message = "響應(yīng)體為空";

break;

case ResponseCode.SERVER_ERROR:

message = "服務(wù)器錯(cuò)誤";

break;

case ResponseCode.JSON_SERIALIZATION:

message = "JSON序列化錯(cuò)誤";

break;

case ResponseCode.EXIT_SUCCESS:

message = "退出成功";

break;

case ResponseCode.REQUEST_FAILED:

message = "請(qǐng)求發(fā)送失敗";

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();

}

}

工具類有三個(gè)方法,分別是:

加密:將密碼進(jìn)行SHA3-512加密,加密后的密碼再發(fā)送到后端

獲取對(duì)應(yīng)信息:根據(jù)Message獲取對(duì)應(yīng)的提示信息

展示信息:利用Toast展示信息

4.6 響應(yīng)體+響應(yīng)碼

響應(yīng)體:

@NoArgsConstructor

@Setter

@Getter

public class RestResponse {

private int code;

private Object data;

}

響應(yīng)碼:

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 請(qǐng)求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());

//構(gòu)造OkHttp請(qǐng)求Request

Request request = new Request.Builder().url(NetworkSettings.SIGN_IN_UP).post(

//請(qǐng)求體類型為application/json;charset=utf-8,利用了Jackson序列化為JSON

RequestBody.create(mapper.writeValueAsString(new User(name, password)), mediaType)

).build();

//異步POST操作,傳入一個(gè)Callback回調(diào)

client.newCall(request).enqueue(new Callback() {

//若失敗

@Override

public void onFailure(@NotNull Call call, @NotNull IOException e) {

//請(qǐng)求失敗信息

message.what = ResponseCode.REQUEST_FAILED;

//展示對(duì)應(yīng)信息,注意不能直接使用Toast.make(getApplicationContext(),"message",Toast.LENGTH_SHORT).show()

//因?yàn)椴皇峭粋€(gè)線程,需要使用Handler提交,也就是post()方法,參數(shù)為一個(gè)線程

handler.post(()->Utils.showMessage(getApplicationContext(),message));

e.printStackTrace();

}

@Override

public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {

//如果成功

if (response.isSuccessful()) {

//獲取請(qǐng)求體

ResponseBody body = response.body();

//如果響應(yīng)體不為空

if (body != null) {

//反序列化為響應(yīng)體,包含了一個(gè)響應(yīng)碼以及數(shù)據(jù)字段

RestResponse restResponse = mapper.readValue(body.string(), RestResponse.class);

//設(shè)置Message

message.what = restResponse.getCode();

//如果登錄成功

if(message.what == ResponseCode.SIGN_IN_SUCCESS){

handler.post(()->{

//存儲(chǔ)用戶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));

//保存舊用戶名以及舊密碼在更新的時(shí)候使用

oldName = binding.name.getText().toString();

oldPassword = binding.password.getText().toString();

});

}

} else {

//空響應(yīng)體

message.what = ResponseCode.EMPTY_RESPONSE;

Log.e("RESPONSE_BODY_EMPTY", response.message());

}

} else {

//服務(wù)器錯(cuò)誤

message.what = ResponseCode.SERVER_ERROR;

Log.e("SERVER_ERROR", response.message());

}

//根據(jù)Message提示對(duì)應(yīng)信息

handler.post(()->Utils.showMessage(getApplicationContext(),message));

}

});

} catch (JsonProcessingException e) {

message.what = ResponseCode.JSON_SERIALIZATION;

Utils.showMessage(getApplicationContext(),message);

e.printStackTrace();

}

}

這部分是登錄注冊(cè)的代碼,還有更新用戶信息以及刪除用戶的代碼,大部分類似。

5 測(cè)試

6 注意事項(xiàng)

如果出現(xiàn)了問題某些功能不能正常實(shí)現(xiàn)可以參考此處的一些注意事項(xiàng)以及解決方案。

7 源碼

提供了Java+Kotlin兩種實(shí)現(xiàn):

8 參考鏈接

如果覺得文章好看,歡迎點(diǎn)贊。

同時(shí)歡迎關(guān)注微信公眾號(hào):氷泠之路。

總結(jié)

以上是生活随笔為你收集整理的安卓后端mysql_后端Spring Boot+前端Android交互+MySQL增删查改(Java+Kotlin实现)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。