SpringBoot框架一般分为View层、Controller层、Service层、Mapper层、pojo层。
View层:视图层,根据接到的数据展示页面给用户
Controller层:响应用户需求,决定用什么视图,需要准备什么数据来显示。Controller层负责前后端交互,接收前端请求,调用Service层,接收Service层返回的数据,最后返回具体的数据和页面到客户端
Service层:Service层也可以分为三个方面
(1)接口:用来声明方法
(2)继承实现接口
(3)impl:接口的实现(将mapper和service进行整合的文件)
Service层存放业务逻辑处理,有一些关于数据库处理的操作,但是不是直接和数据库打交道,有接口,
也有接口的实现方法,在impl实现接口类中需要导入mapper类,mapper层是直接与数据库进行操作的。
Mapper层:也可以称为DAO层,是数据库CRUD的接口,只有方法名,具体实现在mapper.xml文件中,对数据库进行数据持久化操作(把数据放到持久化的介质中,同时提供CRUD操作)
src/main/resource文件夹中的mapper.xml文件,里面存储的是真正的数据库CRUD语句
Pojo层:存放实体类,与数据库中的属性基本保持一致,一般包括getter、setter、toString方法(未使用插件lombok的情况下)
controller层(处理前台发送的请求)--->service定义接口(业务逻辑)--->serviceImpl(对接口函数进行实现)
--->mapper(Mapper接口,方法名与Mapper.xml中定义的statement的id相同)--->mapper.xml(写sql语句查询数据库)
由此可见,Service层在Mapper层之上,在Controller层之下,既调用Mapper接口,又提供接口给Controller层用。
分层后,访问数据库和进行Service之间分工明确,对Service的需求修改,无需修改Mapper层,如果有访问数据库的新需求,也只需要在Mapper层修改。
mapper层一般单独的为一个包
扫描mapper第一种方式
每个mapper接口上添加@Mapper注解 这个注解会通过spring boot启动注解自动扫描
扫描mapper第二种方式
在main函数中使用@MapperScan(basePackages = “com.demo.mapper”)标注dao所在的包名,这种方式如果引入会有红色波浪线问题 再加上@Repository这个注解就没有了
两种方式选择其一就行。我实践的是方式1
@RestController注解表示每个方法返回的数据将直接写入响应体
我们有每个操作的路由(@GetMapping、@PostMapping、@PutMapping和@DeleteMapping,对应于 HTTP GET、POST、PUT和DELETE调用)
接口如何处理传参,我们可以使用@RequestParam
注解实现获取前端URL中的参数。具体使用见例子
url请求:("/basic/user?name=小红") 此时会将name=小红传递给方法的name参数,多个参数使用&连接。
@GetMapping(value = "/user_info")
public List getUser(@RequestParam(value = "name") String name) {
//此时我们可以通过定义的name属性获取url中属性为name的值。
}
spring boot可以使用spring.banner.location属性更改程序启动时打印的横幅。比如默认的spring boot图形,我们在resources目录下新建banner.txt将需要打印的内容写在其中即可
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
**************************************************************************************
${application.title} ${application.version}
Base on Spring Boot ${spring-boot.version}
**************************************************************************************
System.properties:
--------------------------------------------------------------------------------------
java.specification.version = ${java.specification.version}
java.specification.vendor = ${java.specification.vendor}
java.specification.name = ${java.specification.name}
java.vm.specification.version = ${java.vm.specification.version}
java.vm.specification.vendor = ${java.vm.specification.vendor}
java.vm.specification.name = ${java.vm.specification.name}
java.home = ${java.home}
java.version = ${java.version}
java.vendor = ${java.vendor}
java.vendor.url = ${java.vendor.url}
java.vm.version = ${java.vm.version}
java.vm.vendor = ${java.vm.vendor}
java.vm.name = ${java.vm.name}
java.class.version = ${java.class.version}
java.class.path = ${java.class.path}
java.library.path = ${java.library.path}
java.io.tmpdir = ${java.io.tmpdir}
java.ext.dirs = ${java.ext.dirs}
os.name = ${os.name}
os.arch = ${os.arch}
os.version = ${os.version}
user.name = ${user.name}
user.home = ${user.home}
user.dir = ${user.dir}
使用时在application.proerties中配置banner.txt文件的位置。不配置也会默认加载。
spring.banner.location=classpath:banner.txt
运行查看效果
其客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:
GET用来获取资源,
POST用来新建资源(也可以用于更新资源),
PUT用来更新资源,
DELETE用来删除资源;
其利用URL进行资源请求,根据http响应码,判断请求状态,进而做出提醒。
URI = scheme “/” host “:” port “/” path [ “?” query ][ “#” fragment ]
scheme: 指底层用的协议,如http、https、ftp
host: 服务器的IP地址或者域名
port: 端口,http默认为80端口
path: 访问资源的路径,就是各种web 框架中定义的route路由
通常一个RESTful API的path组成如下:
/{version}/{resources}/{resource_id}
version:API版本号,有些版本号放置在头信息中也可以,通过控制版本号有利于应用迭代。
resources:资源,RESTful API推荐用小写英文单词的复数形式。
resource_id:资源的id,访问或操作该资源。
query: 对于需要传递的参数可以使用?分隔后加到后面,比如key=value等
查询字符串,为发送给服务器的参数,在这里更多发送数据分页、排序等参数。
fragment: 锚点,定位到页面的资源
1:url命名规范
接口传参可以使用@RequestParam注解实现,@RequestParam将查询字符串参数的值绑定属性名到方法的参数属性中。此查询字符串参数不是required。如果请求中不存在,则使用defaultValue
接口传参可以使用@RequestParam 或者@RequestBody
@RequestParam 传参可以是单独的参数;
@RequestBody则是吧参数都封装为了一个bean对象进行传参
以@RequestParam举例
@PostMapping (value = "/insert")
public String insert(@RequestParam (value = "host") String host,@RequestParam (value = "port") String port,@RequestParam (value = "email") String email,@RequestParam (value = "report_name") String report_name,@RequestParam (value = "password") String password) {
String sql = String.format("insert into insert_email_getaway_config_info(host,port,email,password,report_name) values ('%s','%s','%s','%s','%s')", host, port, email, password, report_name);
System.out.println(sql);
hsqlDB.insertData(sql);
return "insert success";
}
接口调用,可以使用postman测试接口
url格式如下,后面参数和path用?分隔,各个参数间用&分隔
/insert?host=127.0.0.1&port=8888&email=1&password=1&report_name=小红
一般建议用@RequestBody封装对象来传参,利于维护!!!
@PostMapping(value = "/insert_email_getaway_config")
public String insertEmailGetawayConfig(@RequestBody EmailGetawayBean email) {
//获取值 email.getEmail();
}
@Data
public class EmailGetawayBean {
String host, port, email, password, report_name;
}
//接口使用举例 http://localhost:9999/view/get_info?type=sms
@GetMapping(value = "/get_system_config")
public List<systemConfig> getSystemConfigInfo(@RequestParam(required = false) String type) {//使用type}
用于对应数据库对应的属性,必须提供对应的getter,setter,toString方法。如果使用lombok使用@data注解即可实现。
映射方式1:
Spring boot查询数据库底层使用mybatis进行交互,如果没有明确写明实体类和表的字段映射,就是同名对应,匹配成功则数据交互成功,匹配失败则数据交互失败,导致接收空值。
@Data
public class user{
String name,age,like,addr;
}
映射方式2:使用@data+@TableField
如:把表中的id映射到bean的user_id字段
@TableField(value="id")
private string user_id
多表关联查询存在字段重名或者不一致问题可以使用这种方式解决,不想使用@TableField也可以在mapper.xml配置中使用resultMap来完成映射。详见第四节
底层利用mybatis和数据库进行交互,提供CRUD增删改查操作。
单独创建mapper的包放mapper类,使用注解@Mapper
利用@Select(“${sqlStr}”)创造一个查询语句,将查询结果映射为user
@Param(“sqlStr”) 注解为传参映射作用
@Mapper
public interface QueryHsqlMapper {
//根据路由和传参的sql语句执行数据库操作
@Select("${sqlStr}")
List getData(@Param("sqlStr") String sqlStr);
}
利用类上的@Mapper注解可实现springboot启动时自动扫描。
使用步骤如下
@MapperScan(basePackages = "com.demo.mapper")
@SpringBootApplication()
public class Main {
private static final Logger LOG = LoggerFactory.getLogger(Main.class.getName());
public static void main(String[] args) throws IOException, InterruptedException {
SpringApplication.run(Main.class, args);
}
mybatis.mapper-locations=classpath*:/mapper/*Mapper.xml
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.cache-enabled=false
1:创建接口
2:实现接口,使用注解@Slf4j @Service
public interface QueryHsqlDB {
List getData(String sqlStr);
}
@Slf4j
@Service
public class QueryHsqlDBImple implements QueryHsqlDB {
@Resource
QueryHsqlMapper mapper;
@Override
public List getData(String sqlStr) {
List data = mapper.getData(sqlStr);
return data;
}
}
定义请求的方式(get/put/post/delete),请求的路径等,使用注解
@RestController
@CrossOrigin
@RequestMapping({“/data”}) //定义url访问的路径
@RestController
@CrossOrigin
@RequestMapping({"/data"})
public class Controller {
@Resource
private QueryHsqlDB queryHsqlDB;
@GetMapping(value = "/user")
public List getuser() {
String sql="select * from user";
return queryHsqlDB.getData(sql)
}
访问时路径即可/data/user即会调用getuser方法,将返回结果传给客户端
此种方式查询的sql是写死的,存在硬编码的问题,后续进行优化。
springboot采用properties文件作为总配置文件,默认application.properties放在src/main/resource文件,在、该配置文件中可以使用通配符*用来表示相同类型的文件。比如多个Mapper.xml
下面介绍一些常用配置
# tomcat启动也就是访问web的端口号
server.port=11080
#xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mybatis.mapper-locations=classpath*:/mapper/*Mapper.xml
# 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
mybatis.configuration.map-underscore-to-camel-case=true
#jdbc数据库相关配置
#指定数据库驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库jdbc连接url地址,serverTimezone设置数据库时区东八区
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/login?characterEncoding=UTF-8&serverTimezone=GMT%2B8
#数据库账号
spring.datasource.username=root
spring.datasource.password=root
数据库的创建表和插入数据
#建表,利用*实现模糊匹配
spring.datasource.schema=classpath:create*.sql
#插入数据
#进行该配置后,每次启动程序,程序都会运行resources/insert.sql文件,对数据库的数据操作,相当于往表中插入数据。
spring.datasource.data=classpath:insert*.sql
#前端的静态资源加载,一般为resoources路径下的文件,个人创建/resources/templates目录存放
spring.resources.static-locations=classpath:templates/
spring.mvc.view.suffix=.html
# 自定义属性用于配置启动时自动打开浏览器
openProject.isOpen=true
openProject.web.openUrl=http://localhost:${server.port}/#/
自定义属性配置
#自定义属性
com.name="111"
com.name1="222"
# 取值需要在类上使用@Configuration注解后再使用@Value(value="${com.name}")的方式获取赋值并使用
@Value(value="${com.name}")
private String name;//既可以将name值赋予变量
windows和linux均可,使用@Configuration完成和配置文件的映射,用@Value(“${属性}”) 获取属性的值
先在application.properties中定义属性
server.port=9999
openProject.web.openUrl=http://localhost:${server.port}/#/
@Configuration
public class GetLinuxIp {
@Value("${openProject.web.openUrl}") //完成属性映射
private static String url;
private static final Logger LOG = LoggerFactory.getLogger(GetLinuxIp.class.getName());
public static String getInet4Address() { //获取运行服务器的ip
Enumeration nis;
String ip = null;
try {
nis = NetworkInterface.getNetworkInterfaces();
for (; nis.hasMoreElements();) {
NetworkInterface ni = nis.nextElement();
Enumeration ias = ni.getInetAddresses();
for (; ias.hasMoreElements();) {
InetAddress ia = ias.nextElement();
//ia instanceof Inet6Address && !ia.equals("")
if (ia instanceof Inet4Address && !ia.getHostAddress().equals("127.0.0.1")) {
ip = ia.getHostAddress();
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return ip;
}
public static void AutoOpenUrl() throws InterruptedException, IOException {//打开服务器的逻辑实现
Runtime run = Runtime.getRuntime();
String os = System.getProperty("os.name").toLowerCase();
if (os.indexOf("win") >= 0) { //判断操作系统
try {
run.exec("rundll32 url.dll,FileProtocolHandler " + url);
} catch (Exception e) {
e.printStackTrace();
LOG.error(e.getMessage());
}
} else if (os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0) {
String currentIp = GetLinuxIp.getInet4Address();
String[] browsers = {"firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape"};
String browser = null;
for (int count = 0; count < browsers.length && browser == null; count++) {
if (Runtime.getRuntime().exec(
new String[]{"which", browsers[count]}).waitFor() == 0) {
browser = browsers[count];
}
}
if (browser != null) {
Runtime.getRuntime().exec(new String[]{browser, url.replaceAll("localhost", currentIp)});
}
}
LOG.info("启动浏览器打开项目成功:" + url);
}
}
一般用于测试或者demo中的数据模拟,比如结合hsqldb等内存数据库,表的加载可以在application.properties中进行使用,详见步骤5。
比如创建用户表:create.sql
CREATE TABLE user_info(
user_id varchar(255) NOT NULL PRIMARY KEY, // PRIMARY KEY设置主键
name varchar(255) DEFAULT NULL,
age varchar(255) DEFAULT NULL,
like varchar(255) DEFAULT NULL,
addr varchar(255) DEFAULT NULL
);
进行数据插入:insert.sql
insert into user_info values("001","小红","18","篮球","西安");
SpringApplication提供了一种方便的方法来引导从main()方法启动的 Spring 应用程序。在许多情况下,您可以委托给静态SpringApplication.run方法,如下例所示:
如果在mapper类中已经使用了@mapper注解此处就不需要@MapperScan(basePackages = “com.demo.mapper”)
@MapperScan(basePackages = "com.demo.mapper") //用于扫描mapper文件,一般指定到包路径,在mapper层使用@mapper注解同理两种均可
@SpringBootApplication() //main函数的固定注解
public class Main {
private static final Logger LOG = LoggerFactory.getLogger(Main.class.getName());
public static void main(String[] args) throws IOException, InterruptedException {
SpringApplication.run(Main.class, args); //指定启动的主类即可
LOG.info("started......");
} }
常用测试工具
JUnit :单元测试 Java 应用程序的事实标准。
Spring Test & Spring Boot Test:Spring Boot 应用程序的实用程序和集成测试支持。
AssertJ:一个流畅的断言库。
Hamcrest:匹配器对象库(也称为约束或谓词)。
Mockito:Java 模拟框架。
JSONassert : JSON 的断言库。
JsonPath:JSON 的 XPath。
@SpringBootTest 指定测试类在SpringBoot环境下运行
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Main.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ControllerTest {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();//建议使用这种
}
@Test /*用于模拟接口请求查看返回的结果*/
public void testGet() throws Exception {
ResultActions perform = mockMvc.perform(MockMvcRequestBuilders.get("/data"));
String contentAsString = perform.andReturn().getResponse().getContentAsString();
System.out.println(contentAsString);
Assert.assertTrue(contentAsString.contains("小红"));
}
连接mysql的话需要引入相关依赖。
springboot常用依赖说明
依赖说明:
1:mybatis和mybatis-plus只引入其中一个包就行了,不要重复
2:结合spring boot引入的必须是 **
**不能是mybatis-plus-core。否则会导致加载mapper.xml配置文件失败,报**Invalid bound statement (not found)**
org.example
smhll_redis_restful
1.0-SNAPSHOT
smhll_redis_restful
http://www.example.com
UTF-8
1.7
1.7
UTF-8
1.8
1.2.41
1.5.15.RELEASE
4.2.0
1.18.20
2.5
2.1.2
3.11
junit
junit
4.13.2
test
org.springframework.boot
spring-boot-starter-test
${springboot}
test
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybatis.version}
org.springframework.boot
spring-boot-starter-web
${springboot}
org.springframework.boot
spring-boot-devtools
true
${springboot}
org.springframework.boot
spring-boot-starter
${springboot}
org.springframework.boot
spring-boot-starter-data-redis
${springboot}
com.alibaba
fastjson
${fastjson}
com.google.code.gson
gson
org.hsqldb
hsqldb
org.apache.commons
commons-lang3
${commons.version}
org.projectlombok
lombok
${lombok.version}
provided
org.slf4j
slf4j-api
1.7.25
org.slf4j
slf4j-log4j12
1.7.21
org.springframework.boot
spring-boot-maven-plugin
repackage
org.apache.maven.plugins
maven-assembly-plugin
jar-with-dependencies
com.demo.Main
3.3.0
org.apache.maven.plugins
maven-compiler-plugin
8
关于第三节中controller中的sql硬编码问题进行优化。
采用sql放在mapper.xml中进行维护,后期更新配置不用改代码
MyBatis的关注点在POJO与SQL之间的映射关系,mapper.xml就是实现这种关系的。里面还可以加if判断,foreach循环等操作。接口类型包括select查询,insert插入,delete删除等等对于sql语句。
<?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.training.mapper.UserMapper">
<!--定义接口方法对应的 SQL 语句,传参通过mapper类传入-->
<select id="findByName" parameterType="String" resultType="com.training.entity.User">
select * from user where name=#{name}
</select>
<!--多个传参时parameterType可以忽略,取值通过下标取值-->
<select id="getByNameAge" resultType="com.xf.pojo.User">
select * from t_user where name = #{0} and age = #{1}
</select>
</mapper>
namespace:配置我们mapper类的位置
select id:配置我们的mapper类中的查询方法名,
parameterType:定义传参的类型
resultType:sql查询结果映射到那个pojo类
对于pojo字段映射麻烦的关联查询可以使用resultMap 映射
<resultMap type="customer" id="customerMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
</resultMap>
<!--type是pojo类名,id是定义resultMap 自己的id,用于在写sql模块时resultType属性进行配置-->
springboot+mybatis+mysql 用mapper.xml映射文件中的 namespace 和 select 标签的 id 去需要我们是mapper类中的方法 解决1:修改正确的配置文件完成映射 使用的mybatis maven依赖不能是 1:查看相关注解是否添加,以及是否正确 网上已经有很多文章说明可能导致这个报错的原因,无非是以下几种: 2.Mapper.java的方法在Mapper.xml中没有,然后执行Mapper的方法会报此 3.xxxMapper.java的方法返回值是List,而select元素没有正确配置ResultMap,或者只配置ResultType 4.mapper.xml中配置是否正确,返回值传参和bean对象是否能映射上,映射失败会报问题。 我的问题是在mapper.xml中指定了databaseId=“mysql”,删除后就正常运行了 传入的参数和sppring容器中的参数映射失败了,在mapper,service层的方法参数上都加上**(@Param(“value”) String value)**,对传参加入注解完成映射就可以了
数据写入mysql,mysql表设置了自增主键,现在从spring写接口进行数据插入。
1:controller层传参使用封装的bean对象,不用传主键
2:mapper将bean对象传入
3:mapper.xml配置 <insert id="id" useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_user(id,username,password) VALUES(#{id}, #{username}, #{password});
</insert>
5:问题汇总
5.1:Invalid bound statement (not found) .mapper文件
原因1:配置文件中的包名和方法名可能写错找不到方法
原因2:mapper.xml配置文件压根没有找到。
解决2:查看配置文件的路径,同时必须在application.properties中进行配置。pom.xml依赖包必须用**
**不能是mybatis-plus-core。否则会导致加载mapper.xml配置不到,报这种问题5.2:a compent requied a bean of “自己定义的mapper类”,配置加载不到spring容器中
mybatis-plus-boot-starter
,必须是**
**。5.3:对于所有的找到不到mapper类或者加载不到配置文件的问题
2:application.yaml配置中是否配置mybatis.mapper-locations或者mybatis-plus.mapper-locations
用哪个必须和自己引用的依赖包是一致,否则找不到
3:resource下定义的*.mapper.xml中配置namespace 和 select 标签的 id 必须正确
4:mybatis引入的maven依赖必须是两个之一,不需要其他mybatis依赖了<artifactId>mybatis-plus-spring-boot-starter</artifactId>`
<artifactId>mybatis-spring-boot-starter</artifactId>`
5.4:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
1.检查xml文件的namespace是否正确,id和方法名是否一致5.5:Parameter ‘xxx’ not found. Available parameters are [arg1, arg0, param1, param2].