创建一个数据库表 这里的字段user_name跟java实体类中的 userName 不对应,但是符合驼峰转换。
CREATE TABLE `tb_user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_name` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
`age` int DEFAULT NULL,
`sex` int DEFAULT NULL,
`birthday` date DEFAULT NULL,
`created` date DEFAULT NULL,
`updated` date DEFAULT NULL,
`note` varchar(2000) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
数据库表拆入的内容如图:
首先创建一个spirngboot工程 pom文件中依赖父工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wqs</groupId>
<artifactId>springboot-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
</project>
创建springboot默认的配置文件application.properties 在resources目录下
创建实体类
package com.kkb.application.pojo;
import java.util.Date;
public class User {
private Long id;
// 用户名
//自动转换下换线到驼峰命名user_name -> userName
private String userName;
// 密码
private String password;
// 姓名
private String name;
// 年龄
private Integer age;
// 性别,1男性,2女性
private Integer sex;
// 出生日期
private Date birthday;
// 创建时间
private Date created;
// 更新时间
private Date updated;
// 备注
private String note;
//get 和set 方法 tostring 方法省略
}
1.整合jdbc
引入jdbc依赖,和测试依赖。
测试方法生成快捷键ctrl+shift+t 选择junit4 选择生成会在test文件夹下出现一个同级别类型的文件名称,只是方法的类名多了个 xxxTest
springboot的测试注解要添加在测试类上 分别是
@RunWith(SpringRunner.class) @SpringBootTest
<!--整合jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--springboot测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--mysql连接驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
application.properties文件中的内容 数据库连接参数
# 连接四大参数
spring.datasource.url=jdbc:mysql://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=123456
dao层是操作数据库的方法
BeanPropertyRowMapper会对字段属性进行驼峰映射 使用mybatis时不会进行驼峰映射,需要进行 映射 处理。
package com.kkb.application.dao;
import com.kkb.application.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
@Repository
public class JdbcDao {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 查询结果如果是一个字段(列的一个值)直接使用queryForObject(sql,Integer.class); (sql语句,类.class)
* @return
*/
public int getCount(){
String sql="select count(id) from tb_user";
return jdbcTemplate.queryForObject(sql,Integer.class);
}
/**
* 查询结果如果有多个字段 使用queryForMap(sql) 返回的结果类型为 @return
* key max(tid) value 数值
* key min(tid) value 数值
* @return Map<String, Object>
*/
public Map<String, Object> getMany(){
String sql="select max(id),min(id) from tb_user";
return jdbcTemplate.queryForMap(sql);
}
/**
* 查询所有 BeanPropertyRowMapper会对字段属性进行驼峰映射 user_name -> userName
* @return
*/
public List<User> findAll() {
return jdbcTemplate.query("select * from tb_user", new BeanPropertyRowMapper<>(User.class));
}
/**
* 根据id主键查询
* @param id
* @return
*/
public User findById(int id) {
return jdbcTemplate.queryForObject("select * from tb_user where id=?",
new Object[]{id},
new BeanPropertyRowMapper<>(User.class));
}
/**
* 增加
* @param user
* @return
*/
public Integer insert(User user){
String sql="insert into tb_user(name,created) value (?,?)";
return jdbcTemplate.update(sql,user.getName(),user.getCreated());
}
/**
* 修改
* @param user
* @return
*/
public int update(User user){
String sql="update team set tname=? ,createed=? where tid=?";
return jdbcTemplate.update(sql,user.getName(),user.getCreated(),user.getId());
}
/**
* 删除
* @param id
* @return
*/
public int del(int id){
String sql="delete from team where tid=?";
return jdbcTemplate.update(sql,id);
}
}
测试类:
package com.kkb.application.dao;
import com.kkb.application.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import java.util.Map;
import java.util.Set;
@RunWith(SpringRunner.class)
@SpringBootTest
public class JdbcDaoTest {
@Autowired
private JdbcDao jdbcDao;
@Test
public void findAll() {
List<User> list = jdbcDao.findAll();
for (User user : list) {
System.out.println(user);
}
}
@Test
public void findById() {
User byId = jdbcDao.findById(1);
System.out.println(byId);
}
@Test
public void getMany(){
Map<String, Object> many = jdbcDao.getMany();
Set<Map.Entry<String, Object>> entries = many.entrySet();
for (Map.Entry<String, Object> entry : entries) {
System.out.println("键:"+entry.getKey() +"值:"+entry.getValue());
}
}
}
注意:
测试类的文件名称要和被测试的方法所在的类的文件名称相同除了多个Test,不然会报错误。
2 .整合SpringMVC
pom中引入依赖 web启动器 其中包含tomcat
<!--为了让SpringBoot帮我们完成各种自动配置,
我们必须引入SpringBoot提供的自动配置依赖,我们称为 启动器 。-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
springboot实现了mvc自动配置,这里我们主要解决以下3个问题
修改端口
静态资源
拦截器配置
修改端口
修改端口号是在application.properties配置文件中设置,设置为80后 可以使用localhost域名直接访问。
# 映射端口
server.port=80
访问静态资源
ResourceProperties的类,里面就定义了静态资源的默认查找路径:
默认的静态资源路径为:
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public
只要静态资源放在这些目录中任何一个,SpringMVC都会帮我们处理。
我们习惯会把静态资源放在 classpath:/static/ 目录下。我们创建目录,并且添加一些静态资源:
项目启动,直接通过localhost/s11.jpg就可以访问到图片。
添加拦截器
拦截器也是我们经常需要使用的,在SpringBoot中该如何配置呢?
首先我们定义一个拦截器:
package com.kkb.application.Interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("处理器执行前执行!");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.debug("处理器执行后执行!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.debug("跳转后执行!");
}
}
通过实现 WebMvcConfigurer 并添加 @Configuration 注解来实现自定义部分SpringMvc配置:
package com.kkb.application.config;
import com.kkb.application.Interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
/**
* 通过@Bean注解,将我们定义的拦截器注册到Spring容器
*
* @return
*/
@Bean
public LoginInterceptor loginInterceptor() {
return new LoginInterceptor();
}
/**
* 重写接口中的addInterceptors方法,添加自定义拦截器
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 通过registry来注册拦截器,通过addPathPatterns来添加拦截路径
registry.addInterceptor(this.loginInterceptor()).addPathPatterns("/**");
}
}
ant path路径匹配通配符
‘?’ 匹配任何单字符
‘*’ 匹配0或者任意数量的字符
‘/**’ 匹配0或者更多的目录
接下来运行并查看日志:
你会发现日志中什么都没有,因为我们记录的log级别是debug,默认是显示info以上,我们需要进行配置。
SpringBoot通过 logging.level.*=debug 来配置日志级别,*填写包名
# 设置com.lxs包的日志级别为debug
logging.level.com.kkb.application=debug
目录结构
启动项目,访问controller中的一个方法mapping 比如localhost/hello
这时控制台的日志打印输出如下:
整合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.wqs.pojo
# mapper.xml文件位置,如果没有映射文件,请注释掉
mybatis.mapper-locations=classpath:mappers/*.xml
接口
package com.kkb.dao;
import com.kkb.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserDao {
public List<User> findAll();
}
映射文件
<?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.UserDao">
<select id="findAll" resultType="user">
select * from tb_user
</select>
</mapper>
Mapper的加载接口代理对象方式有2种
第一种:使用@Mapper注解(不推荐)
需要注意,这里没有配置mapper接口扫描包,因此我们需要给每一个Mapper接口添加 @Mapper 注解,才能被识别。
@Mapper
public interface UserMapper {
}
第二种设置MapperScan,注解扫描的包(推荐)
@MapperScan("dao所在的包"),自动搜索包中的接口,产生dao的代理对象
@SpringBootApplication
@MapperScan("com.lxs.demo.dao")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
项目结构:
测试:
package com.kkb.dao;
import com.kkb.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserDaoTest {
@Autowired
private UserDao userDao;
@Test
public void testFindAll() {
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
}
}
测试结果:
这里的实体类中tostring方法重写后并没有全部输出属性,这是输出四个属性,这里可以看到mybatis处理字段跟属性不一致的情况时,默认情况下不会进行驼峰式转换。需要进行映射处理。
整合tk-mybaits
使用Mybatis时,最大的问题是,要写大量的重复SQL语句在xml文件中,除了特殊的业务逻辑SQL语句之外,还有大量结构类似的增删改查SQL。而且,当数据库表结构改动时,对应的所有SQL以及实体类都需要更改。这大量增加了程序员的负担。避免重复书写CRUD映射的框架有两个
通用mybatis(tk mybatis)
mybatis plus,通能更加强大
通用Mapper的作者也为自己的插件编写了启动器,我们直接引入即可:
<!-- 通用mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
实体类与数据字段的映射
tk mybatis 实体类使用的注解是jpa注解
@Table("tb_user")
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //主键 自增加策略
@Column(name = "user_name") //支持驼峰命名转换
private String userName;
@Transient //不于表中字段进行映射
private Stirng address;
}
1. 默认表名=类名,字段名=属性名
2. 表名可以使用 @Table(name = "tableName") 进行指定
3. 字段和属性的映射通过@Column(name = "fieldName")添加在属性上 指定数据库中对应的字段
4. 使用 @Transient 注解表示该属性跟字段不进行映射
继承tk.mybatis中的Mapper类实现单表的基本操作
Mapper类中包含单表的的所有简单操作,但想通过多表进行连接查询时或者复杂的sql语句时(比如复杂的模糊查询),需要自己写对应xml文件。
@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 test="note != null">
and note like '%${note}%'
</if>
</where>
</select>
</mapper>
方法使用说明
继承了Mapper,继承的XXXMapper就拥有了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 条件删除数据
下面需要将接口进行代理
需要使用@Mapper 或者 @MapperScan注解
注意:如果使用mapperScan注解而不是Mapper要把MapperScan类改成tk-mybatis构件的类
例如:@MapperScan("com.lxs.demo.dao") 加到springBoot启动类Application上。
@MapperScan和@Mapper的区别就是 :后者是单个类接口的实现,前者是包扫描的接口的实现的。
application.properties文件中的配置
# mybatis 别名扫描
mybatis.type-aliases-package=com/kkb/pojo
# mapper.xml文件位置,如果没有映射文件,请注释掉
mybatis.mapper-locations=classpath:mappers/*.xml
注意要把MapperScan类改成tk-mybatis构件的类
mport tk.mybatis.spring.annotation.MapperScan;
/**
* 项目启动类
*/
@SpringBootApplication
@MapperScan("com/kkb/dao") // 生成接口代理类的注解
public class webApplication {
public static void main(String[] args) {
SpringApplication.run(webApplication.class, args);
}
}
测试类:
package com.kkb.dao;
import com.kkb.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import tk.mybatis.mapper.entity.Example;
import java.util.Date;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userDao;
@Test
public void findByUser() {
User user = new User();
user.setName("王");
user.setNote("java");
List<User> byUser = userDao.findByUser(user);
byUser.forEach(user1 -> System.out.println(user1));
}
@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", "%王%");
userDao.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);
}
}
注意:
这里自己定义的多条件查询是基于mybatis实现的,如果实体类中的属性跟字段名称不相等,查询到的值就会为null,这里使用@Column注解并不会起作用,毕竟是tk-mybatis注解,这时候解决这个问题就要就与mybatis方法,方法有两种 别名或者resultMapper映射。
上面的方法findByUser()就是因为实体类的userName 跟字段 user_name对不上出现值为null
整合mybatis-plus
Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,避免了我们重复CRUD语句。
创建工程,引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wqs</groupId>
<artifactId>springboot-mp</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mybatisplus.version>3.3.2</mybatisplus.version>
<skipTests>true</skipTests>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件application.yml
首先需要借助一个插件 (plugin) JBLSpringBootAppGen 会自动生成启动类和默认的application.yml文档。
# DataSource Config
spring:
datasource:
driver-class-name: org.h2.Driver
schema: classpath:db/schema-h2.sql
data: classpath:db/data-h2.sql
url: jdbc:h2:mem:test
username: root
password: test
#logger Config
logging:
level:
com.wqs: debug
数据库脚本文件/db/data-h2.sql和/db/schema-h2.sql(拷贝)
h2数据库是一个基于内存的数据库,在jvm启动时,自动执行脚本加载相应的数据
springboot 中使用h2数据库直接按照上面配置,配置schema表结构脚本和data数据脚本即可
注意这里用户名密码可以省略不写,或者随意设定。
启动类
注意:mp的MapperScan注解使用的是mybatis的类不是tk-mybatis,这里跟tk的扫描不同
package com.wqs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com/wqs/mapper")
public class quickApplication {
public static void main(String[] args) {
SpringApplication.run(quickApplication.class, args);
}
}
实体类
package com.wqs.pojo;
import lombok.Data;
import lombok.experimental.Accessors;
//加上此注解,不用写实体类中的get和set方法
@Data
//注解能够在属性赋值的基础上再次赋值 对象.setxxx().setxxx(); 返回的是实体类对象
@Accessors(chain = true)
@TableName("user2")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
dao
package com.wqs.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wqs.pojo.User;
public interface UserMapper extends BaseMapper<User> {
}
常用注解
MyBatisPlus提供了一些注解供我们在实体类和表信息出现不对应的时候使用。通过使用注解完成逻辑上匹配,mp使用驼峰转换。
@TableName 实体类的类名和数据库表名不一致
@TableId 实体类的主键名称和表中主键名称不一致
@TableId注解是专门用在主键上的注解,如果数据库中的主键字段名和实体中的属性名,不一样且不是驼峰之类的对应关系,可以在实体中表示主键的属性上加@Tableid注解
@TableField 实体类中的成员名称和表中字段名称不一致> 使用 @TableField(exist = false)
当属性跟数据库中的字段对不上时用@TableFialue = "字段名称")
mybatis plus注解策略配置
mysql自增主键注解策略设置如下,默认采用雪花算法生成全局唯一主键 ASSIGN_ID(3)
@TableId( value= " " , type = IdType.AUTO)
private Long id;
程序结构:
mybatis-plus中BaseMapper中的接口方法:
方法说明:
内置增删改查
插入一个数据
/**
* 插入一个数据
*/
@Test
public void testInsert() {
User user = new User();
user.setName("开课吧");
user.setEmail("lxs@163.com");
user.setAge(3);
Assert.assertTrue(userMapper.insert(user) > 0);
userMapper.selectList(null).forEach(System.out :: println);
}
删除
主键删除deleteById 单个删除
条件删除delete 多个删除,只要符合条件都会进行删除
多条件删除又有三种方法:
1.利用 QueryWrapper包装类进行条件约束
2.Wrappers包装类进行条件约束,跟方法1的形式一样,只是调用的类不同
3.Wrappers包装类加lambda表达式 与方法1和方法2 的不同点在与like条件约束里面的第一个不是属性的指定,是利用函数式接口调用get方法进行属性指定。
//主键删除
userMapper.deleteById(3l);
userMapper.selectList(null).forEach(System.out :: println);
//批量删除:1
userMapper.delete(new QueryWrapper<User>().like("name", "J"));
userMapper.selectList(null).forEach(System.out :: println);
//批量删除:2
userMapper.delete(Wrappers.<User>query().like("name", "J"));
userMapper.selectList(null).forEach(System.out :: println);
//批量删除:3
userMapper.delete(Wrappers.<User>query().lambda().like(User::getName, "J"));
userMapper.selectList(null).forEach(System.out :: println);
修改
通过主键的单个修改 updateById方法 和 指定列的多个修改update方法
这里的id为long类型 赋值时需要加上后缀 “l”
批量修改1指定修改的列为email, 修改的值为huike@163.com like是做条件的约束,只有name字段中含有“J”的才进行修改。
批量修改2 方法与方法1作用一样,不同点是,没有利用set方法进行列和值 的指定,而是通过传递一个实体类。指定要修改列(属性)和修改后的值。
//基本修改
userMapper.updateById(new User().setId(1l).setName("慧科"));
userMapper.selectList(null).forEach(System.out :: println);
//批量修改:1
userMapper.update(null, Wrappers.<User>update().set("email", "huike@163.com").like("name","J"));
userMapper.selectList(null).forEach(System.out :: println);
//批量修改:2
userMapper.update(new User().setEmail("huike@163.com"), Wrappers.<User>update().like("name", "J"));
userMapper.selectList(null).forEach(System.out :: println);
查询
分为基本查询 和 投影查询
基本查询:
单个查询:通过seleOne eq方法指定"属性" 和 "值" ,符合的条件的作为查询结果。
多个查询: 通过selectList QueryWrapper赋值为null 为查询所有
userMapper.selectList(null).forEach(System.out :: println);
投影查询:
在多个查询的基础上进行属性的约束,只将select约定的 "列或属性" 查询,没有指定的赋值为null。
// //基本查询 单个
System.out.println(userMapper.selectOne(Wrappers.<User>query().eq("name", "Tom")));
//基本查询 所有
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
//投影查询
userMapper.selectList(new QueryWrapper<User>().select("id", "name")).forEach(user -> {
System.out.println(user);
});
测试结果;
插入一条数据
testInsert方法生成的id是采用雪花算法生成的。 使用雪花算法最好使用long类型的主键 对应的数据库使用BIGINT类型
mp的分页
1.mp内置的分页
2.自定义xml分页
3.pageHelper 分页 (最好用的分页工具)
方法1:
一.设置分页拦截器
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
// 开启 count 的 join 优化,只针对 left join !!!
return new PaginationInterceptor().setCountSqlParser(new JsqlParserCountOptimize(true));
}
}
二.调用分页方法
selectPage方法
参数1 Page对象,用来限定起始页码和每页显示行数 。
参数2 QueryWrapper对象或者Wrapper对象 查询条件条件的限定。
@Test
public void testPage() {
System.out.println("------ baseMapper 自带分页 ------");
Page<User> page = new Page<>(1, 5);
//IPage<User> pageResult = userMapper.selectPage(page, Wrappers.query());
IPage<User> pageResult = userMapper.selectPage(page, new QueryWrapper<User>().eq("age", 331));
//分页时候getTotal获取不到值 有三种解决方案
System.out.println("总条数 ------> " + pageResult.getTotal());
System.out.println("当前页数 ------> " + pageResult.getCurrent());
System.out.println("当前每页显示数 ------> " + pageResult.getSize());
// 分页对象记录列表 个数为 5 从page设置起始页码值开始,符合条件的5个
pageResult.getRecords().forEach(System.out :: println);
}
解决方案:
1.配置类中每有加@Configuration 注解,spring容器读取不到拦截器对象
2.设置断言mysql,该方法已经过时。
PaginationInterceptor page = new PaginationInterceptor();
page.setDialectType("mysql");
3.检查pom.xml是否有pageHelper依赖,有则去除
结果:
方法2:
自定义xml分页
一.application.yml配置文件 因为用到了自定义的xml方法,需要在yml文件中进行路径和别名的映射。
这里配置的是别名搜索和加载映射文件,其他是之前配置的
# DataSource Config
spring:
datasource:
driver-class-name: org.h2.Driver
schema: classpath:db/schema-h2.sql
data: classpath:db/data-h2.sql
url: jdbc:h2:mem:test
username: root
password: test
#logger Config
logging:
level:
com.wqs: debug
# 配置mybatis plus
mybatis-plus:
type-aliases-package: com.wqs.pojo #别名搜索
mapper-locations: classpath:/mappers/*.xml #加载映射文件
UserMapper接口自定义方法
package com.wqs.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wqs.pojo.User;
import org.apache.ibatis.annotations.Param;
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);
}
UserMapper.xml映射文件 放在resouses 下的 mappers文件夹下,文件名称和实体类的方法接口相同。
<?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.wqs.mapper.UserMapper">
<sql id="selectSql">
SELECT
*
FROM
user2
</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>
测试:
/**
*xml自定义分页
*/
@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 = userMapper.selectUserByPage(page, user);
System.out.println("总条数 ------> " + pr.getTotal());
System.out.println("当前页数 ------> " + pr.getCurrent());
System.out.println("当前每页显示数 ------> " + pr.getSize());
pr.getRecords().forEach(System.out :: println);
}
结果:
limit后面是个 ? 这里没有截全 (屏幕实在是拉取不动了)
方法3:
pageHelper分页
一.引入pageHelper依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
二.mybatis plus 整合pageHelper的配置类
这里不会跟mp的分类拦截器冲突
/**
* 两个分页插件都配置,不会冲突
* 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>
UserMapper的方法名称
public List<User> selectUserByPage2(User conditioin);
四.测试
测试方法
1.使用mp的内置方法selectList进行属性绑定,like约email定模糊查询,eq约定 age字段的值
包装类使用QuryWrapper 或者Wrappers对象都可以
2. 使用自定义的xml方法
@Test
public void testPageHelper() {
//创建一个查询条件 xml自定义方法使用
User u = new User();
u.setAge(331);
u.setEmail("test");
PageInfo<User> page = PageHelper.startPage(1, 5).doSelectPageInfo(() ->
//使用MP的内置方法 也可以
userMapper.selectList(new QueryWrapper<User>().like("email","test").eq("age",331))
// Wrappers对象
// userMapper.selectList(Wrappers.<User>query().like("email","test").eq("age",331))
// 使用自定义的 xml方法
// userMapper.selectUserByPage2(u)
);
page.getList().forEach(System.out::println);
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()));
}
结果:
查询语句:
整合thmleaf
直接引入启动器:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
SpringBoot会自动为Thymeleaf注册一个视图解析器:
与解析JSP的InternalViewResolver类似,Thymeleaf也会根据前缀和后缀来确定模板文件的位置:
默认前缀: classpath:/templates/
默认后缀: .html
所以如果我们返回视图: users ,会指向到 classpath:/templates/users.html
一般我们无需进行修改,默认即可。
这里需要注意的是controller类中不要加@RestController 和 ResponseBody注解 ,否者视图解析器不会生效,前段也无法获取request作用域的值(model.addAttribute("users",all);),也就是返回类型为Stirng类型的情况下,返回的是字符串。前段获取的不是视图地址。
创建一个html页面
根据上面的文档介绍,模板默认放在classpath下的templates文件夹,我们新建一个users.html文件放入其中.
注意,把html 的名称空间,改成: xmlns:th="http://www.thymeleaf.org"
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
<style type="text/css">
table {border-collapse: collapse; font-size: 14px; width: 80%; margin: auto}
table, th, td {border: 1px solid darkslategray;padding: 10px}
</style>
</head>
<body>
<div style="text-align: center">
<span style="color: darkslategray; font-size: 30px">欢迎光临!</span>
<hr/>
<table class="list">
<tr>
<th>id</th>
<th>姓名</th>
<th>用户名</th>
<th>年龄</th>
<th>性别</th>
<th>生日</th>
<th>备注</th>
<th>操作</th>
</tr>
<tr th:each="user, status : ${users}" th:object="${user}">
<td th:text="${user.id}">1</td>
<td th:text="*{name}">张三</td>
<td th:text="*{userName}">zhangsan</td>
<td th:text="${user.age}">20</td>
<td th:text="${user.sex} == 1 ? '男': '女'">男</td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd')}">1980-02-30</td>
<td th:text="${user.note}">1</td>
<td>
<a th:href="@{/delete(id=${user.id}, userName=*{userName})}">删除</a>
<a th:href="|/update/${user.id}|">修改</a>
<a th:href="'/approve/' + ${user.id}">审核</a>
</td>
</tr>
</table>
</div>
</body>
</html>
编写UserService,调用UserMapper的查询所有方法
package com.kkb.service;
import com.kkb.dao.UserMapper;
import com.kkb.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class userService {
@Autowired
private UserMapper userMapper;
public List<User> findAll(){
return userMapper.selectAll();
}
}
编写一个controller,用来查询用户数据
package com.kkb.controller;
import com.kkb.pojo.User;
import com.kkb.service.userService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
public class userController {
@Autowired
private userService userService;
@RequestMapping("all")
public String findAll(Model model){
List<User> all = userService.findAll();
model.addAttribute("users",all);
return "users";
}
}
结果展示:
模板缓存
Thymeleaf会在第一次对模板解析之后进行缓存,极大的提高了并发处理能力。但是这给我们开发带来了不便,修改页面后并不会立刻看到效果,我们开发阶段可以关掉缓存使用
# 开发阶段关闭thymeleaf的模板缓存
spring.thymeleaf.cache=false
注意:在Idea中,我们需要在修改页面后按快捷键: Ctrl + Shift + F9 对项目进行rebuild才可以。
我们可以修改页面,测试一下。
thmleaf的语法
表达式
它们分为三类
1. 变量表达式
2. 选择或星号表达式
3. URL表达式
变量表达式
变量表达式即OGNL表达式或Spring EL表达式(在Spring中用来获取model attribute的数据)。
如下所示:
${session.user.name}
与th:text 指令配合使用 <span th:text="${user.text}">你好 thymleaf</span>
选择(星号)表达式
选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器(map)来执行,
被指定的object由th:object属性定义.指定后 直接使用 *{属性} 调用赋值即可 前面不需要再加user对象。
<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>
URL表达式
URL表达式指的是把一个有用的上下文或回话信息添加到URL,这个过程经常被叫做URL重写。 @{/order/list}
URL还可以设置参数: @{/order/details(id=${orderId}, name=*{name})}
相对路径:@{../documents/report}
三种形式
1.url表达式
<a th:href="@{/delete(id=${user.id}, userName=*{userName})}">删除</a>
2.文本替换
<a th:href="|/update/${user.id}|">修改</a>
3.字符串拼接
<a th:href="'/approve/' + ${user.id}">审核</a>
内嵌变量
为了模板更加易用,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>