1.整合SpringMVC
上篇文章已经能实现mvc自动配置,这里我们主要解决以下3个问题
修改端口
静态资源
拦截器配置
修改端口
查看 SpringBoot 的全局属性可知,端口通过以下方式配置:
# 映射端口
server.port = 80 重启服务
访问静态资源
ResourceProperties 的类,里面就定义了静态资源的默认查找路径:
添加拦截器:
1 首先写一个类来 implements HandlerInterceptor,重写里面的 prehandle posthandle afterhandle 方法
2 通过实现 WebMvcConfigurer ,然后重写接口中的addInterceptors方法,添加自定义拦截器 并添加 @Configuration 注解来实现自定义部分SpringMvc配置:
路径匹配通配符
‘?’ 匹配任何单字符
‘*’ 匹配 0 或者任意数量的字符 (只是一层,/test1/* 比如test1/test.html 可以拦截 ,但是/test1/test2/test3.html不可以拦截
‘/**’ 匹配 0 或者更多的目录(可以多层拦截)
你会发现日志中什么都没有,因为我们记录的log级别是debug,默认是显示info以上,
SpringBoot 通过
logging.level.*=debug 来配置日志级别, * 填写包名
# 设置 com.lxs 包的日志级别为 debug
logging.level.com.lxs = debug
2.整合jdbc
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
当然, 不要忘了数据库驱动 , SpringBoot 并不知道我们用的什么数据库,这里我们选择 MySQL :
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
配置连接池
其实,在刚才引入 jdbc 启动器的时候, SpringBoot已经自动帮我们引入了一个连接池: HikariCP
目前速度最快的连接池了,我们看看它与 c3p0 的对比: 因此,我们只需要指定连接池参数即可:
# 连接四大参数
spring.datasource.url = jdbc : mysql : //localhost : 3306/
spring.datasource.username = root
spring.datasource.password = root
# 可省略, SpringBoot 自动推断
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.hikari.idle-timeout = 60000
spring.datasource.hikari.maximum-pool-size = 30
spring.datasource.hikari.minimum-idle = 10
示例:
1 建立实体类(比如User类)
2 建立dao ,UserDao类 ,JDBC的不用实现类,直接就 用jdbcTemplate就可以
3.整合mybatis
mybatis
SpringBoot 官方并没有提供 Mybatis 的启动器,不过 Mybatis 官网 自己实现了:
<!--mybatis -->
<dependency>
<groupId> org.mybatis.spring.boot </groupId>
<artifactId> mybatis-spring-boot-starter </artifactId>
<version> 1.3.2 </version>
</dependency>
配置
# mybatis 别名扫描
mybatis.type-aliases-package = com.zx.dao
# mapper.xml 文件位置 , 如果没有映射文件,请注释掉
mybatis.mapper-locations = classpath :/ mappers/*.xml
示例:
1 建立实体类(比如User类)
接口
3配置映射文件 (在resource下的mappers 建立UserDaoMapper)
4 整合通用:mybatis(tk mybatis)
概念
使用 Mybatis 时,最大的问题是,要写大量的重复 SQL 语句在 xml 文件中,除了特殊的业务逻辑 SQL 语句之外,还有
大量结构类似的增删改查 SQL 。而且,当数据库表结构改动时,对应的所有 SQL 以及实体类都需要更改。这大量增
加了程序员的负担。避免重复书写 CRUD 映射的框架有两个
1 mybatis plus ,通能更加强大,后面实战项目中讲解
2 通用mybatis( tk mybatis )
通用Mapper的作者也为自己的插件编写了启动器,我们直接引入即可:
<!-- 通用 mapper -->
<dependency>
<groupId> tk.mybatis </groupId>
<artifactId> mapper-spring-boot-starter </artifactId>
<version> 2.0.2 </version>
</dependency>
tk mybatis 实体类使用的注解是jpa注解
@Table(name = "tb_user") public class User implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 用户名 private String userName; .........
注意事项:
1. 默认表名 = 类名,字段名 = 属性名
2. 表名可以使用 @Table(name = "tableName") 进行指定
3. @Column(name = "fieldName") 指定
4. 使用 @Transient 注解表示跟字段不进行映射
不需要做任何配置就可以使用了。
@Mapper
public interface UserMapper extends tk.mybatis.mapper.common.Mapper<User>{
public List<User> findByUser(User user);
}
自定义映射文件
映射复杂方法 resources/mappers/UserMapper.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.lxs.demo.dao.UserMapper">
<select id="findByUser" resultType="user">
SELECT
*
FROM
tb_user
<where>
<if test="name != null">
name like '%${name}%'
</if><if test="note != null">
and note like '%${note}%'
</if>
</where>
</select>
</mapper>
一旦继承了 Mapper , 继承的Mapper就拥有了Mapper所有的通用方法:
Select
方法:
List<T> select(T record)
;
说明:根据实体中的属性值进行查询,查询条件使用等号
方法:
T selectByPrimaryKey(Object key)
;
说明:根据主键字段进行查询,方法参数必须包含完整的主键属性,
查询条件使用等号
方法:
List<T> selectAll()
;
说明:查询全部结果,
select(null)
方法能达到同样的效果
方法:
T selectOne(T record)
;
说明:根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异
常,查询条件使用等号
方法:
int selectCount(T record)
;
说明:根据实体中的属性查询总数,查询条件使用等号
Insert
方法:
int insert(T record)
;
说明:保存一个实体,
null
的属性也会保存,不会使用数据库默认值
方法:
int insertSelective(T record)
;
说明:保存一个实体,
null
的属性不会保存,会使用数据库默认值
Update
方法:
int updateByPrimaryKey(T record)
;
说明:根据主键更新实体全部字段,
null
值会被更新
方法:
int updateByPrimaryKeySelective(T record)
;
说明:根据主键更新属性不为
null
的值
Delete
方法:
int delete(T record)
;
说明:根据实体属性作为条件进行删除,查询条件使用等号
方法:
int deleteByPrimaryKey(Object key)
;
说明:根据主键字段进行删除,方法参数必须包含完整的主键属性
Example
方法
方法:
List<T> selectByExample(Object example)
;
说明:根据
Example
条件进行查询 重点:这
个查询支持通过
Example
类指定查询列,通过
selectProperties
方法指定查询列
方法:
int selectCountByExample(Object example)
;
说明:根据
Example
条件进行查询总数
方法:
int updateByExample(@Param("record") T record, @Param("example") Object example)
;
说明:根据
Example
条件更新实体
record
包含的全部属性,
null
值会被更新
方法:
int updateByExampleSelective(@Param("record") T record, @Param("example") Object example)
;
说
明:根据
Example
条件更新实体
record
包含的不是
null
的属性值
方法:
int deleteByExample(Object example)
;
说明:根据
Example
条件删除数据
注意要把MapperScan类改成tk-mybatis构件的类import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@EnableConfigurationProperties
@MapperScan
("com.lxs.demo.dao")
public class Application {
示例测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserDaoTest {
@Autowired
private UserDao userDao;
@Test public void testFindByUser() {
User condition = new User();
condition.setName("a");
List<User> list = userMapper.findByUser(condition);
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testFindAll() {
List<User> list = userDao.selectAll();
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testFindById() {
User user = userDao.selectByPrimaryKey(4);
System.out.println(user);
}
@Test
public void testFindByExample() {
Example example = new Example(User.class); example.createCriteria().andLike("name", "%a%"); userMapper.selectByExample(example).forEach(user -> {
System.out.println(user);
});
}@Test
public void testInsert() {
User user = new User();
user.setAge(18);
user.setBirthday(new Date());
user.setCreated(new Date());
user.setName("周星驰");
userDao.insert(user);
}
5.Thymeleaf
概念
Thymeleaf 是一个跟 FreeMarker 类似的模板引擎,它可以完全替代 JSP 。
特 点:
1 动静结合: Thymeleaf 在有网络和无网络的环境下皆可运行,无网络显示静态内容,有网络用后台得到数据 替换静态内容
2 SpringBoot 完美整合, springboot 默认整合 thymeleaf
直接引入启动器:
<dependency>
<groupId> org.springframework.boot </groupId>
<artifactId> spring-boot-starter-thymeleaf </artifactId>
</dependency>
5.1 入门案例
编写接口
编写 UserService ,调用 UserMapper 的查询所有方法
@Service public class UserService { @Autowired private UserDao userDao; public List<User> queryAll() { return this.userDao.selectAll(); } }
编写一个controller,返回一些用户数据,放入模型中,等会在页面渲染 编写一个controller,返回一些用户数
据,放入模型中,等会在页面渲染
@Controller public class UserController { @Autowired private UserService userService; @RequestMapping("/all") public String all(Model model) { List<User> list = userService.findAll(); model.addAttribute("users", list); // 返回模板名称(就是classpath:/templates/目录下的html文件名) return "users"; } }
SpringBoot 会自动为 Thymeleaf 注册一个视图解析器:
5.2 thymeleaf页面:
把 html 的名称空间,改成: xmlns:th="http://www.thymeleaf.org" 会有语法提示(th: XX)
5.3模板缓存
Thymeleaf会在第一次对模板解析之后进行缓存,极大的提高了并发处理能力。但是这给我们开发带来了不便,修
改页面后并不会立刻看到效果,我们开发阶段可以关掉缓存使用:
# 开发阶段关闭 thymeleaf 的模板缓存
spring.thymeleaf.cache = false
关闭了 就可以使用点击 🔨锤子标志 进行重新编译,页面就会变化,不用重启启动器
6 thymeleaf详解
表达式
它们分为三类
1. 变量表达式
2. 星号表达式
3. URL表达式
1 变量表达式 :
$ { session . user . name }
它们将以 HTML 标签的一个属性来表示: <h5> 表达式 </h5>
<span>${text}</span>
<span th:text="${text}"> 你好 thymleaf</span>
2 星号表达式
选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器 (map) 来执行,如下: *
{customer.name}
被指定的 object 由 th:object 属性定义:
<tr th:each="user : ${users}" th:object="${user}">
<td th:text="${user.id}">1</td>
<td th:text="*{name}"> 张三 </td>
<td th:text="*{userName}">zhangsan</td>
3URL表达式
URL 表达式指的是把一个有用的上下文或回话信息添加到 URL ,这个过程经常被叫做 URL 重写。
@{/order/list}
URL 还可以设置参数:
@{/order/details(id=${orderId}, name=*{name})} 相对路径:
@{../documents/report}
让我们看这些表达式:
<form th:action="@{/createOrder}">
<a href="main.html" th:href="@{/main}">
url 表达式
<a th:href="@{/delete(id=${user.id}, userName=*{userName})}">删除</a>
文本替换(两个竖线||)
<a th:href="| /update/${user.id} |"> 修改 </a>
字符串拼接('/XX/'+${XXX})
<a th:href="'/approve/' + ${user.id}"> 审核 </a>
表达式常见用法
字面( Literals )
文本文字( Text literals ) : 'one text', 'Another one!',…
数字文本( Number literals ) : 0, 34, 3.0, 12.3,…
布尔文本( Boolean literals ) : true, false
空( Null literal ) : null 文字标记( Literal tokens ) : one, sometext, main,…
文本操作( Text operations )
字符串连接 (String concatenation): +
文本替换(Literal substitutions): |The name is ${name}|
算术运算( Arithmetic operations )
二元运算符( Binary operators ) : +, - , *, /, %
减号(单目运算符) Minus sign (unary operator): -
布尔操作( Boolean operations )
二元运算符(Binary operators): and, or
布尔否定(一元运算符): !, not
比较和等价 (Comparisons and equality)
比较( Comparators ) : >, <, >=, <= (gt, lt, ge, le)
等值运算符( Equality operators ) : ==, != (eq, ne)
条件运算符( Conditional operators )
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
常用th标签:
基本用法
1.
赋值、字符串拼接
字符串拼接还有另外一种简洁的写法
<a th:href="|/update/${user.id}|">
修改
</a>
<a th:href="'/approve/' + ${user.id}">
审核
</a>
2. 条件判断 If/Unless
Thymeleaf 中使用 th:if和th:unless 属性进行条件判断,下面的例子中, <a> 标签只有在 th:if 中条件成立时才显 示:
<h5>if
指令
</h5>
<a th:if="${users.size() > 0}">
查询结果存在
</a><br>
<a th:if="${users.size() <= 0}">
查询结果不存在
</a><br>
<a th:unless="${session.user != null}" href="#">
登录
</a><br>
th:unless
于
th:if
恰好相反,只有表达式中的条件不成立,才会显示其内容。
也可以使用
(if) ? (then) : (else)
这种语法来判断显示的内容
3. for 循环
内联文本:
[[…]]
内联文本的表示方式,使用时,必须先用
th:inline=”text/javascript/none”
激活,
th:inline
可以在
父级标签内使用,甚至作为
body
的标签。内联文本尽管比
th:text
的代码少,不利于原型显示。
<h5>
内联
js</h5>
<script th:inline="javascript">
var text = '[[${text}]]';
alert(text);
</script>
6. 内嵌变量
为了模板更加易用,
Thymeleaf
还提供了一系列
Utility
对象(内置于
Context
中),可以通过
#
直接访问:
使用+#
dates
:
java.util.Date**
的功能方法类。
calendars :
类似
#dates
,面向
java.util.Calendar
numbers :
格式化数字的功能方法类
strings :
字符串对象的功能类,
contains,startWiths,prepending/appending
等等。
objects:
对
objects
的功能类操作。
bools:
对布尔值求值的功能方法。
arrays
:
对数组的功能类方法。
lists:
对
lists
功能类方法
sets
maps
下面用一段代码来举例一些常用的方法:
dates
<h5>
内置变量
</h5>
<h6 th:text="${#dates.createNow()}">
获取当前日期
</h6>
strings
<h5>
内置变量
</h5>
<h6 th:text="${#dates.createNow()}">
获取当前日期
</h6>
<h6 th:text="${#strings.substring(text, 6, 9)}">
截取字符串
</h6>
<h6 th:text="${#strings.length(text)}">
获得长度
</h6>
<h6 th:text="${#strings.randomAlphanumeric(6)}">
随机字符串
</h6>
<h6 th:text="${#strings.equals(text, 'hello text....')}"></h6>
使用步骤
1 创建实体类User
2 编写mapper(dao),接口实现 BaseMapper<User>{
}
常用注解
MyBatisPlus
提供了一些注解供我们在实体类和表信息出现不对应的时候使用。通过使用注解完成逻辑上匹
配。
mybatis plus注解策略配置
如果
mysql
自增主键注解策略设置如下
@TableId(type = IdType.AUTO)
private Long id;
默认主键策略
/**
*
采用雪花算法生成全局唯一主键
**/
排除实体类中非表字段
使用 @TableField(exist = false) 注解
主键策略参考源码 IdType
内置增删改查:
@Test
public void testInsert() {
User user = new User();
user.setName("开课吧");
user.setEmail("lxs@163.com");
user.setAge(3);
Assert.assertTrue(mapper.insert(user) > 0); mapper.selectList(null).forEach(System.out :: println);
}@Test
public void testDelete() {
// //主键删除 // mapper.deleteById(3l);
// mapper.selectList(null).forEach(System.out :: println);
// //批量删除: // mapper.delete(new QueryWrapper<User>().like("name", "J"));
mapper.selectList(null).forEach(System.out :: println);
}
@Test
public void testUpdate() {
// //基本修改
// mapper.updateById(new User().setId(1l).setName("慧科"));
mapper.update(new User().setEmail("huike@163.com"), Wrappers.<User>update().like("name", "J"));
mapper.selectList(null).forEach(System.out :: println);
}
@Test
public void testSelect() {
// //基本查询
// System.out.println(mapper.selectOne(Wrappers.<User>query().eq("name", "Tom"))); //投影查询
mapper.selectList(new QueryWrapper<User>().select("id", "name")).forEach(user -> { System.out.println(user);
});
}
分页:
内置分页
必须把PaginationInterceptor 用bean生成对象放到spring容器中
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
// 开启 count 的 join 优化,只针对 left join !!!
return new PaginationInterceptor().setCountSqlParser(new JsqlParserCountOptimize(true));
}
}
#本来生成的count语句像这样
select count(1) from (select u.id,ua.account from user u left join user_account ua on
u.id=ua.uid)
这时候分页查
count
时,其实可以去掉
left join
直查
user
,因为
user
与
user_account
是
1
对
1
关系,如下:
查
count:
select count(1) from user u
测试
@Test
public void testPage() {
System.out.println("------ baseMapper 自带分页 ------");
Page<User> page = new Page<>(1, 5);
IPage<User> pageResult = mapper.selectPage(page, new QueryWrapper<User>().eq("age", 20));
System.out.println("总条数 ------> " + pageResult.getTotal());
System.out.println("当前页数 ------> " + pageResult.getCurrent());
System.out.println("当前每页显示数 ------> " + pageResult.getSize());
pageResult.getRecords().forEach(System.out :: println);
}
自定义xml分页
1 application.yml配置文件
#
配置
mybatis plus
mybatis-plus:
type-aliases-package: com.lxs.crud.entity #
别名搜索
mapper-locations: classpath:/mappers/*.xml #
加载映射文件
2 UserMapper接口
public interface UserMapper extends BaseMapper<User> {
/**
* 如果映射的接口方法有2个参数需要@Param定义参数名,定义参数名后,映射文件中使用p.属性 c.属性,具体访
问
*
* @param page
* @param conditioin
* @return
*/
public IPage<User> selectUserByPage(@Param("p") IPage<User> page, @Param("c") User
conditioin);
}
3 UserMapper.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.zx.mapper.UserMapper">
<sql id="selectSql">
SELECT
*
FROM
user
</sql>
<select id="selectUserByPage" resultType="user">
<include refid="selectSql"></include>
<where>
<if test="c.age !=null">
age = #{c.age}
</if>
<if test="c.email !=null">
and email like '%${c.email}%'
</if>
</where>
</select>
</mapper>
测试
@Test
public void testXmlPage() {
System.out.println("------ baseMapper 自定义xml分页 ------");
Page<User> page = new Page<>(1, 5);
User user = new User();
user.setAge(20);
user.setEmail("test");
IPage<User> pr = mapper.selectUserByPage(page, user);
System.out.println("总条数 ------> " + pr.getTotal());
System.out.println("当前页数 ------> " + pr.getCurrent());
System.out.println("当前每页显示数 ------> " + pr.getSize());
pr.getRecords().forEach(System.out :: println);
}
3 pageHelper分页
引入
pageHelper
依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
mybatis plus 整合pageHelper的配置类
@Configuration
@MapperScan("com.lxs.mybatisplus.samples.crud.mapper")
public class MybatisPlusConfig {
/**
* mp分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
// 开启 count 的 join 优化,只针对 left join !!!
return new PaginationInterceptor().setCountSqlParser(new JsqlParserCountOptimize(true));
}
/**
* 两个分页插件都配置,不会冲突
* pagehelper的分页插件
*/
@Bean
public PageInterceptor pageInterceptor() {
return new PageInterceptor();
}}
映射文件
<select id="selectUserByPage2" resultType="user">
<include refid="selectSql"></include>
<where>
<if test="age !=null">
age = #{age}
</if>
<if test="email !=null">
and email like '%${email}%'
</if>
</where>
</select>
测试
@Test
public void testPageHelper() {
// pagehelper
// PageInfo<User> page = PageHelper.startPage(1, 2).doSelectPageInfo(() ->
mapper.selectList(Wrappers.<User>query()));
PageHelper.startPage(1,2);
// PageInfo<User> page = new PageInfo<>(mapper.selectList(Wrappers.<User>query()));
User u = new User();
u.setAge(20);
PageInfo<User> page = new PageInfo<User>(mapper.selectUserByPage2(u));
List<User> list = page.getList();
System.out.println("总行数=" + page.getTotal());
System.out.println("当前页=" + page.getPageNum());
System.out.println("每页行数=" + page.getPageSize());
System.out.println("总页数=" + page.getPages());
System.out.println("起始行数=" + page.getStartRow());
System.out.println("是第一页=" + page.isIsFirstPage());
System.out.println("是最后页=" + page.isIsLastPage());
System.out.println("还有下一页=" + page.isHasNextPage());
System.out.println("还有上一页=" + page.isHasPreviousPage());
System.out.println("页码列表" + Arrays.toString(page.getNavigatepageNums()));
}
最全SpringBoot基础博客 传送门在下面:
By CaesarChang 张旭
在我脑子里泡了这么久 ,可以点个赞吗 ! 累 ,
么么哒!!!!