Spring Boot专栏四:使用Spring Boot的框架,进行数据库的操作
今天我们来结合专栏二和专栏三的内容,使用Spring Boot的框架,进行数据库的操作。在专栏三中,我们是自顶向下地介绍了框架;本节则是自底向上,通过代码向大家展示如何读取数据库中的数据。
数据库中的内容
本节我们仍然使用到第二节中创建好的user_类,内容也不变,具体内容不再赘述,请大家自行查阅,链接为:数据库操作
创建一个Model
最底层的东西当然是我们的模型啦,其实也就是一个类,或者是一个实体(如果大家会画ER图,一定会对这个实体有印象吧)。所有类应当放到同一个文件夹中,我个人习惯把这个文件夹叫做entity,有些人可能会叫model或者bean,这个没有关系,只是习惯问题。
于是,我们在main/java/com/example/demo下创建entity文件夹,在entity文件夹中建一个类,因为我们现在仍然对用户进行操作,所以创建一个叫做User的类吧。
POJO
我们之前介绍了POJO,因此我们需要
- 对mysql表中的每一种字段、属性,都设为User类中的一个私有属性
- 对上述每个属性设置公有的set和get方法
- 添加全参构造器和无参构造器
User类的代码
Spring Boot可以便捷地完成上述这些内容,代码如下:
package com.example.demo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@Log4j
public class User {
@Setter
@Getter
private int user_id;
@Setter
private String user_name;
public String get_user_name() {
return user_name;
}
@Setter
private String user_phone;
public String get_user_phone() {
return user_phone;
}
@Setter
private String user_password;
public String get_user_password() {
return user_password;
}
@Setter double user_wallet;
public double get_user_wallet() {
return user_wallet;
}
@Setter
private String register_time;
public String get_register_time() {
return register_time;
}
@Setter
private String login_time;
public String get_login_time() {
return login_time;
}
public User(String user_name, String user_password) {
this.user_name = user_name;
this.user_password = user_password;
}
public User(String user_name, String user_password, String register_time, String login_time) {
this.user_name = user_name;
this.user_password = user_password;
this.register_time = register_time;
this.login_time = login_time;
}
}
注解的简单解释
可以看到,每个属性都是私有的,同时我们可以用对属性的@Setter和@Getter注解来实现共有的set和get方法,另外用对类的@AllArgsConstructor和@NoArgsConstructor注解来实现全参构造和无参构造。
除此之外,对类还有三个注解,我分别解释一下:@Data注解,表示建了一个POJO类(实际上它包含了此处的其他几个注解);@EqualsAndHashCode,自动生成equals(Object other)和hashCode()方法;@Log4j用来获取日志。
要注意的事情!!!
但大家也发现到一些问题,我自己也觉得我写得不好,我先说一下我哪里写得不好,大家可以一起讨论,也可以指出我的其他缺点:
- 没有使用驼峰规则。确实我没有规范地命名,从数据库的属性命名中就可以看到,我是比较懒散的。希望大家可以规范命名,以后当了项目经理也得带着团队一起规范命名;
- 我这里只有user_id处用了@Getter注解。实际上,在我的实践中,我发现Getter注解实在是不好用,很多时候甚至导致程序出错!大家一定要对于每个属性都自己实现一个get方法。
- 构造函数仍然要自己来写,不管是全参的,还是部分参数的,都根据需求重新写一遍,不要相信@AllArgsConstructor注解!我这里没有写全参的构造函数,是因为可能创建一个User类时,你还不知道user_id是多少,但是如果某个场景下已知user_id,那么我们一定要写上带有user_id的构造函数。
有关于上述的2、3两个问题,有机会我专门开一节来举例子说明(继续挖坑)
Dao层
Dao层是与数据库相关的操作层,在Spring Boot项目中它需要分为两部分来写。
Dao接口
第一个部分,是一些接口。我们在main/java/com/example/demo下创建dao文件夹(也有人叫mapper,这个只是命名习惯,关系不大)。在dao文件夹中,针对于我们的项目,创建一个UserDao.java文件,记得要选择将其定义为一个接口interface,而不是类。
然后,对于这个接口要添加一些注解。在"public interface UserDao {"的上一行,我们添加两个注解:
@Mapper
@Repository(“UserDao”)
这两个注解的意义,可以参考博客Spring 与 Mybatis 中的 @Repository 与 @Mapper
简单来说,@Mapper 是 Mybatis 的注解,和 Spring 没有关系;@Repository 是 Spring 的注解,用于声明一个 Bean。
在 Spring 程序中,Mybatis 需要找到对应的 mapper,在编译的时候动态生成代理类,实现数据库查询功能,所以我们需要在接口上添加 @Mapper 注解。
而 @Repository注解则是用于基于代码的开发,简单来说就是手写 JDBC。这也和我们的下一部分有关。
现在假设我们需要实现最简单的全部查询功能,和根据用户名查询的功能,那么我们就在UserDao.java文件中写这些代码:
package com.example.demo.dao;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository("UserDao")
public interface UserDao {
List<User> show_user_all();
List<User> show_user_by_user_name(@Param("user_name") String user_name);
}
这样就完成了UserDao接口的内容。其中,在第二个方法中有个@Param(“user_name”)注解,实际上它是和JDBC语言有关,我们暂且不提。
写相关的数据库sql语句
下面就要我们手写JDBC的XML代码了。那么,我们在哪里创建文件呢?我在个人实践中也碰到过这个问题,百度了非常久,我记得大概有两天的时间都被这个问题耽搁了。
按照网上的说法,放在任何一个位置都可以,只要文件中的一个地方配置好即可。
但我个人习惯(也是多次尝试后的无奈之举),在main/resources下创建文件夹com,在其中创建文件夹example,在其中创建文件夹demo,在其中创建文件夹dao,在其中创建文件UserDao.xml——那么,这个文件的路径就变为:main/resources/com/example/demo/dao/UserDao.xml,与刚才创建的UserDao接口的路径main/java/com/example/demo/dao/UserDao.java相比,有很高的相似性。
然后,在UserDao.xml中,写如下代码:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.dao.UserDao">
<select id="show_user_all" resultType="com.example.demo.entity.User">
select user_name, user_password, register_time, login_time
from user_
</select>
<select id="show_user_by_user_name" parameterType="String" resultType="com.example.demo.entity.User">
select user_name, user_password, register_time, login_time
from user_
where user_name=#{user_name}
</select>
</mapper>
最需要注意的是第5行:
<mapper namespace="com.example.demo.dao.UserDao">
这句话,就是我上面说的“只要文件中的一个地方配置好即可”的地方。大家如果感兴趣,也可以看看将UserDao.xml放到其他路径下试一试,按理说都是好使的,不过我是不想再折腾了,当时白费两天时间调来调去的……
其他需要注意的部分就是JDBC语句的写法。我相信大家都有sql语句的基础,只是不知道<select …>这些地方怎么写,还有下面的#{user_name}的问题(其实是和Dao接口中的@Param注解有关)。这个部分,就待我另开一节来讲讲吧,今天暂且不提。
Service层
Service层也分为两部分,不过它们处在同一个文件夹下,而且都是用java代码来写的。
Service接口
首先在main/java/com/example/demo下创建service文件夹,根据我们的项目,在其中创建UserService.java,注意,UserService也是个接口。有一个好消息,就是在这里我们不需要添加任何注解!!!
针对于全部查询功能,和根据用户名查询的功能,我们在UserService.java文件中这样写代码:
package com.example.demo.service;
import com.example.demo.entity.User;
import java.util.List;
public interface UserService {
List<User> show_user_all();
List<User> show_user_by_user_name(String user_name);
}
ServiceImpl类
Service接口必须有个实现类。我们在serivce文件夹下新建一个Impl文件夹,在Imple文件夹下创建UserServiceImpl.java,作为UserService接口的实现类,因此在UserServiceImpl类后需要加上 implements UserService.
然后,对于这个类要添加一些注解。在"public class UserServiceImpl implements UserService {"的上一行,我们添加一个注解:
@Service(“UserService”)
用来定义业务层Bean
因为实现类要实现接口的方法,所以我们可以直接重载UserService接口中的方法,具体代码如下:
package com.example.demo.service.impl;
import com.example.demo.dao.UserDao;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("UserService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> show_user_all() {
return userDao.show_user_all();
}
@Override
public List<User> show_user_by_user_name(String user_name) {
return userDao.show_user_by_user_name(user_name);
}
}
我们看到,其中还要通过@Autowired注解创建一个Bean类(接口),是与UserDao有关的,因为业务层实际上是调用UserDao的数据库操作来实现的。
Controller层
controller层是我们能够自主发挥最多的部分了,可以在这里做一些输出、比较等等,反正写起来是比较舒服了。我们之前已经在controller文件夹下创建过TestController.java文件了,因此可以在这个文件夹下继续创建其他相似文件,比如UserController文件。
由于我们要实现全部查询,和根据用户名查询这两个功能,因此我们写两个方法。代码的简单实现如下所示:
package com.example.demo.controller;
import java.util.List;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/show_user_all", method = {RequestMethod.GET})
public List<User> show_user_all() {
List<User> result = userService.show_user_all();
int len = result.size();
System.out.println("共查询到"+len+"条记录");
for (int i = 0;i < len;i++) {
User user = result.get(i);
String user_name = user.get_user_name();
String user_password = user.get_user_password();
String register_time = user.get_register_time();
String login_time = user.get_login_time();
System.out.println("第"+(i+1)+"条记录的用户名为"+user_name+",密码为"+user_password+",注册时间为"+register_time+",最后一次的登录时间为"+login_time);
}
return result;
}
@RequestMapping(value = "/show_user_by_user_name", method = {RequestMethod.GET})
public List<User> show_user_by_user_name(@RequestParam("user_name") String user_name) {
List<User> result = userService.show_user_by_user_name(user_name);
int len = result.size();
System.out.println("共查询到"+len+"条记录");
for (int i = 0;i < len;i++) {
User user = result.get(i);
String user_password = user.get_user_password();
String register_time = user.get_register_time();
String login_time = user.get_login_time();
System.out.println("第"+(i+1)+"条记录的用户名为"+user_name+",密码为"+user_password+",注册时间为"+register_time+",最后一次的登录时间为"+login_time);
}
return result;
}
}
该部分代码的注解比较多,我们全部放到下次讲注解的时候去讲。大家主要学习的就是怎么样得到输入,获取查询结果,并输出查询结果。
结果演示
全部查询功能
启动项目后,在浏览器中输入http://localhost:8080/user/show_user_all,得到如下界面,则为成功。
同时,VSCode终端也应该输出:
根据用户名查询功能
启动项目后,在浏览器中输入http://localhost:8080/user/show_user_by_user_name?user_name=dante,得到如下界面,则为成功。
同时,VSCode终端也应该输出:
通过本节的讲解,我希望大家能够理解Spring Boot是怎么样实现MVC设计模式的,其中数据的流动又是怎么样的。这是一个非常简单的例子。如果想要实现更复杂的功能,我们还需学习一些高级的内容,那就放到下一节讲吧。
另外,为了方便大家拷贝代码,我已经将我的项目放到了github上,项目地址为https://github.com/DTSSDUTDeepLearning/Spring-Boot-Dante,有需求的朋友们自行前往下载。
谢谢大家的阅读。
专栏第五节已经更新啦,传送门:Spring Boot专栏五:Spring Boot中的注解的解释(一)