2020.06.23 更新
1 概述
使用spring boot作为后端框架与Android端配合mysql进行基本的交互,包含了最基本的增删查改功能.
2 开发环境
- Win
- IDEA 2019.2
- Tomcat 9.0.27
- MySQL 8.0.17
- Spring Boot 2.2.1
- JDK 8
3 后端
3.1 新建一个Spring Boot项目
参考这里:
Spring boot+Mysql+Spring data JPA一个Web的Demoblog.csdn.net![d084e520561e25aa5eea288e4f67a74c.png](https://i-blog.csdnimg.cn/blog_migrate/57e80403df01fe4654de18a1fcb0a139.png)
3.2 实体类
新建User类作为实体类:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
用的其实是3.1链接中的代码,里面有详细的解释.
3.3 持久层
新建UserRepository实现增删查改:
@Repository
public interface UserRepository extends CrudRepository<User,Integer>
{
@Query(value = "select * from user where name = ?1",nativeQuery = true)
public List<User> findByName(String name);
@Modifying
@Query(value = "delete from user where name = ?1",nativeQuery = true)
public int deleteByName(String name);
}
由于CrudRepository中已经包含了"增"与"改",所以按需要实现自己的"查"与"删"即可. CrudRepository的api很简单,官方文档在这里. - "增"使用save
即可,参数为实体类 - "删"使用deleteById
,通过主键删除,若不想通过主键删除可以自己编写sql,像上面一样 - "查"使用findAll
或findById
,自定义查找的话需要自己编写SQL - "改"也可使用save
,注意需要设置主键
@Query
用于设置SQL语句,nativeQuery
表示使用原生SQL.
3.4 业务层
新建一个MainService.java:
@Transactional
@Service
public class MainService {
@Autowired
private UserRepository userRepository;
public Iterable<User> getAllUsers()
{
return userRepository.findAll();
}
public List<User> findByName(String name)
{
return userRepository.findByName(name);
}
public boolean add(String name)
{
User user = new User();
user.setName(name);
userRepository.save(user);
return true;
}
public boolean modify(Integer id,String name)
{
User user = new User();
user.setName(name);
user.setId(id);
userRepository.save(user);
return true;
}
public boolean deleteByName(String name)
{
return userRepository.deleteByName(name) != 0;
}
}
getAllUsers()
:返回所有行,Iterable<E>
类型findByName()
:根据name返回所有name相同的行add
直接使用了save
,由于save
返回的是实体类,原本的代码是这样写的:
return userRepository.save(user) != null;
![a96b2cf0ec94b8acb221f3978473dc83.png](https://i-blog.csdnimg.cn/blog_migrate/94bc8c20fb42e143dc25433ad3380dcc.png)
但是文档说了不会为null,所以只能强制返回true了. - modify
使用了id与name作为参数,新建一个user,将其作为setter的参数,然后交给save - deleteByName
使用了自定义的删除函数,返回的是int,在UserRepository中这个int代表SQL影响的行数,删除成功则行数不为0,删除失败,或者没有这行数据则行数为0.因此将返回值与0进行比较
3.5 控制层
@Controller
@RequestMapping(path = "/demo")
public class MainController {
@Autowired
private MainService mainService;
@GetMapping(path = "/getAll")
public @ResponseBody Iterable<User> getAllUsers()
{
return mainService.getAllUsers();
}
@PostMapping(path = "/get")
public @ResponseBody List<User> findByName(String name)
{
return mainService.findByName(name);
}
@PostMapping(path = "/add")
public @ResponseBody boolean add(@RequestParam String name)
{
return mainService.add(name);
}
@PostMapping(path = "/modify")
public @ResponseBody boolean modify(@RequestParam Integer id,@RequestParam String name)
{
return mainService.modify(id,name);
}
@PostMapping(path = "/delete")
public @ResponseBody boolean deleteByName(@RequestParam String name)
{
return mainService.deleteByName(name);
}
}
Controller主要就是几个注解,除了getAllUsers使用Get外,其他的都是用Post.另外就是路径设置,直接在path中设置即可. 后端的话到这里就基本完成了,剩下的打包部署操作就不说了,需要的可以参考这里.
4 Android端
什么新建工程之类的就不说了. 贴上部分MainActivity,完整代码见文末:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
register.setOnClickListener(v ->{new Thread(()-> {
OkHttpClient okHttpClient = new OkHttpClient();
String name = ((EditText) findViewById(R.id.name)).getText().toString();
FormBody formBody = new FormBody.Builder().add("name", name).build();
Request request = new Request.Builder()
.url(Constant.ADD)
.post(formBody)
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
Looper.prepare();
if (Boolean.parseBoolean(response.body().string()))
{
Toast.makeText(this, "注册成功", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(this, "注册失败", Toast.LENGTH_SHORT).show();
}
Looper.loop();
}
//...
}).start();});
login.setOnClickListener(v ->{new Thread(()-> {
OkHttpClient okHttpClient = new OkHttpClient();
String name = ((EditText) findViewById(R.id.name)).getText().toString();
FormBody formBody = new FormBody.Builder().add("name", name).build();
Request request = new Request.Builder()
.url(Constant.GET)
.post(formBody)
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
List<User> users = JSONArray.parseArray(response.body().string(),User.class);
Looper.prepare();
if(users.size() == 0)
{
Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(this,"登录成功",Toast.LENGTH_SHORT).show();
}
Looper.loop();
}
//...
}).start();});
delete.setOnClickListener(v ->{new Thread(()-> {
OkHttpClient okHttpClient = new OkHttpClient();
String name = ((EditText) findViewById(R.id.name)).getText().toString();
FormBody formBody = new FormBody.Builder().add("name", name).build();
Request request = new Request.Builder()
.url(Constant.DELETE)
.post(formBody)
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
Looper.prepare();
if (Boolean.parseBoolean(response.body().string()))
{
Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(this, "删除失败", Toast.LENGTH_SHORT).show();
}
Looper.loop();
}
//...
}).start();});
modify.setOnClickListener(v ->{new Thread(()-> {
OkHttpClient okHttpClient = new OkHttpClient();
String name = ((EditText) findViewById(R.id.name)).getText().toString();
String id = ((EditText)findViewById(R.id.id)).getText().toString();
FormBody formBody = new FormBody.Builder()
.add("name", name)
.add("id",id)
.build();
Request request = new Request.Builder()
.url(Constant.MODIFY)
.post(formBody)
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
Looper.prepare();
if (Boolean.parseBoolean(response.body().string()))
{
Toast.makeText(this, "修改成功", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(this, "修改失败", Toast.LENGTH_SHORT).show();
}
Looper.loop();
}
//...
}).start();});
}
}
下面分别进行CRUD操作.
4.1 增
OkHttpClient okHttpClient = new OkHttpClient();
String name = ((EditText) findViewById(R.id.name)).getText().toString();
FormBody formBody = new FormBody.Builder().add("name", name).build();
Request request = new Request.Builder()
.url(Constant.ADD)
.post(formBody)
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
Looper.prepare();
if (Boolean.parseBoolean(response.body().string()))
{
Toast.makeText(this, "注册成功", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(this, "注册失败", Toast.LENGTH_SHORT).show();
}
Looper.loop();
} catch (IOException e) {
e.printStackTrace();
}
使用OkHttp,通过FormBody
设置参数,然后创建Request
通过OkHttpClient
发送. 由于后端"增"的方法返回的是一个true,因此这里将response.body().string()
转换成boolean
判断是否操作成功. 稍微提一下,
Looper.prepare();
Looper.loop();
这两行可以在非UI线程中使用Toast
.
4.2 删
OkHttpClient okHttpClient = new OkHttpClient();
String name = ((EditText) findViewById(R.id.name)).getText().toString();
FormBody formBody = new FormBody.Builder().add("name", name).build();
Request request = new Request.Builder()
.url(Constant.DELETE)
.post(formBody)
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
Looper.prepare();
if (Boolean.parseBoolean(response.body().string()))
{
Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(this, "删除失败", Toast.LENGTH_SHORT).show();
}
Looper.loop();
} catch (IOException e) {
e.printStackTrace();
}
删这部分也是差不多的,就是改一下url,然后....然后没有了....
4.3 查
OkHttpClient okHttpClient = new OkHttpClient();
String name = ((EditText) findViewById(R.id.name)).getText().toString();
FormBody formBody = new FormBody.Builder().add("name", name).build();
Request request = new Request.Builder()
.url(Constant.GET)
.post(formBody)
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
List<User> users = JSONArray.parseArray(response.body().string(),User.class);
Looper.prepare();
if(users.size() == 0)
{
Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(this,"登录成功",Toast.LENGTH_SHORT).show();
}
Looper.loop();
} catch (IOException e) {
e.printStackTrace();
}
查这里注意一下后端返回的是List
,这里借助阿里的fastjson
转换成List
.
List<User> users = JSONArray.parseArray(response.body().string(),User.class);
然后判断有没有的话就判断长度是否为0即可.
4.4 改
OkHttpClient okHttpClient = new OkHttpClient();
String name = ((EditText) findViewById(R.id.name)).getText().toString();
String id = ((EditText)findViewById(R.id.id)).getText().toString();
FormBody formBody = new FormBody.Builder()
.add("name", name)
.add("id",id)
.build();
Request request = new Request.Builder()
.url(Constant.MODIFY)
.post(formBody)
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
Looper.prepare();
if (Boolean.parseBoolean(response.body().string()))
{
Toast.makeText(this, "修改成功", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(this, "修改失败", Toast.LENGTH_SHORT).show();
}
Looper.loop();
} catch (IOException e) {
e.printStackTrace();
}
改的话只需一个额外的ID参数,在FormBody中add一个即可,不难.
4.5 UI
UI不详细说了,就几个简单的Button,具体可以看代码中的xml文件.
4.6 依赖与其他
![e93c0e58429b1848df37f0ad9c27f23a.png](https://i-blog.csdnimg.cn/blog_migrate/c290be3ac3d25d3d9714d12d66757179.jpeg)
注意一下依赖,还有设置java8.
compileOptions{
sourceCompatibility=1.8
targetCompatibility=1.8
}
dependencies{
implementation 'com.squareup.okhttp3:okhttp:x.x.x'
implementation 'com.alibaba:fastjson:x.x.x'
}
- OkHttp最新版本戳这里查看
- fastjson最新版本戳这里查看
4.7 网络权限
这个笔者之前的文章有说,主要就是AndroidManifest.xml
中的权限设置,请看这里.
5 测试
原始数据库:
![bbb541cb2b572b90ea46072d78a34fdf.png](https://i-blog.csdnimg.cn/blog_migrate/a43f83837985a8206c3f4b653c63d7d9.jpeg)
注册一个:
![bc6cc19fc78a8a42434b0f4197e8fc55.png](https://i-blog.csdnimg.cn/blog_migrate/0622188560db0b65bba3613e7efa0f68.png)
看看数据库:
![8840d39a076af7ad49551b2ef2ae39a3.png](https://i-blog.csdnimg.cn/blog_migrate/1af30ef7c546484838976f8485934728.jpeg)
测试登录:
![318d11d0aedb68cc97512ab3ad369586.png](https://i-blog.csdnimg.cn/blog_migrate/d8aac651f9585b88c920f95b64437de0.png)
试试登录一个不存在的:
![e64db0dcc382e86854a72b153dcfdfa8.png](https://i-blog.csdnimg.cn/blog_migrate/74f867ea6aaccdd01b533d476c3a6c69.png)
修改:
![1f8032a0078fe7b86e226a97d7de49f9.png](https://i-blog.csdnimg.cn/blog_migrate/fea3f66d3ae0afae0d2a8d4aa012cced.jpeg)
![f430c90898897342834ac83d5a48cd71.png](https://i-blog.csdnimg.cn/blog_migrate/f3d5bd40b60dae4c6deee24780bfff74.png)
![2b3eb6b48d0d2b7172eb06838949ff62.png](https://i-blog.csdnimg.cn/blog_migrate/77ebd777f1a3dee914852dc2aa48fa59.jpeg)
最后是删除:
![ac56b65e9c5675465e66244ed064751e.png](https://i-blog.csdnimg.cn/blog_migrate/c6e011ec2855d3e66ad0b09f62923171.png)
![c847bbc6b67815008be6f58587b91341.png](https://i-blog.csdnimg.cn/blog_migrate/6ea54511aa9a85a3084330016cec172e.jpeg)
删除一个不存在的会删除失败.
![c3fd1f7bb34be7f396ece436f081a02c.png](https://i-blog.csdnimg.cn/blog_migrate/7f1bc687f8238b7a24aa1f7ed5afef0e.png)
6 源码
Githubgithub.com 码云gitee.com如果觉得文章好看,欢迎点赞。
同时欢迎关注微信公众号:氷泠之路。
![2fb789e3fbadde25c7fb7f89c81be47e.png](https://i-blog.csdnimg.cn/blog_migrate/49b813bd7d626480f04b3d0bb4b7641d.png)