SSMP框架
约 8284 字大约 28 分钟
(1)Spring框架
spring ioc 注解
@Component用于修饰SpringBoot中的组件,会被组件扫描并生成实例化对象。@Controller、@Service、@Repository都是特殊的组件注解。
@Repository用于修饰dao层的组件,dao层组件专注于系统数据的处理,例如数据库中的数据,同样会被组件扫描并生成实例化对象。
@Service用于修饰service层的组件,service层组件专注于系统业务逻辑的处理,同样会被组件扫描并生成实例化对象。
@Controller用于修饰MVC中controller层的组件,SpringBoot中的组件扫描功能会识别到该注解,并为修饰的类实例化对象,通常与@RequestMapping联用,当SpringMVC获取到请求时会转发到指定路径的方法进行处理。
@PostConstruct用于修饰方法,当对象实例被创建并且依赖注入完成后执行,可用于对象实例的初始化操作。
@PreDestroy用于修饰方法,当对象实例将被Spring容器移除时执行,可用于对象实例持有资源的释放。
@Scope用于声明一个SpringBean实例的作用域,作用域的范围有以下几种:
singleton:单例模式,在Spring容器中该实例唯一,Spring默认的实例模式。
prototype:原型模式,每次使用实例都将重新创建。
request:在同一请求中使用相同的实例,不同请求重新创建。
session:在同一会话中使用相同的实例,不同会话重新创建。
@Scope("singleton")/@Scope(value= ConfigurableBeanFactory.SCOPE_SINGLETON)
@Scope("prototype")/@Scope(value= ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Autowired会根据对象的类型自动注入依赖对象,默认要求注入对象实例必须存在,可以配置required=false来注入不一定存在的对象。
@Qualifier当同一个对象有多个实例可以注入时,使用@Autowired注解无法进行注入,这时可以使用@Qualifier注解指定实例的名称进行精确注入。
@Resource默认会根据对象的名称自动注入依赖对象,如果想要根据类型进行注入,可以设置属性为type = AdminService.class
spring aop 注解
@Aspect用于定义切面,切面是通知和切点的结合,定义了何时、何地应用通知功能。
@Pointcut定义切点表达式,定义了通知功能被应用的范围。
@Before表示前置通知,通知方法会在目标方法调用之前执行,通知描述了切面要完成的工作以及何时执行。
@AfterReturning表示返回通知,通知方法会在目标方法返回后执行。
@Around表示环绕通知,通知方法会将目标方法封装起来,在目标方法调用之前和之后执行自定义的行为。
@AfterThrowing表示异常通知,通知方法会在目标方法返回后执行。
@After表示后置通知,通知方法会在目标方法返回或抛出异常后执行。
@Order用于定义组件的执行顺序,在AOP中指的是切面的执行顺序,value属性越低优先级越高。
spring mvc 注解
@RestController是Controller和ResponseBody的组合
@RequestMapping请求映射
@RequestMapping(value = {"/m1", "/m2"})多个请求映射到一个方法
@PostMapping/@RequestMapping(method = RequestMethod.POST)
@PutMapping/@RequestMapping(method = RequestMethod.PUT)
@PatchMapping/@RequestMapping(method = RequestMethod.PATCH)
@GetMapping/@RequestMapping(method = RequestMethod.GET)
@DeleteMapping/@RequestMapping(method = RequestMethod.DELETE)
@RequestParam用于将http请求参数的值绑定到参数上
@PathVariable用于接收路径参数
@RequestBody用于json转java
@ResponseBody用于java转json
@RequestPart用于接收文件上传中的文件参数,通常是multipart/form-data形式传入的参数。
@RequestHeader是将请求头的信息区数据,映射到功能处理方法的参数上。
@CookieValue是将请求的Cookie数据,映射到功能处理方法的参数上
@ControllerAdvice主要用来处理全局数据,最常见的是结合@ExceptionHandler注解用于全局异常的处理
@RestControllerAdvice是@ControllerAdvice和ResponseBody的组合注解
@ExceptionHandler修饰方法时,表示该方法为处理全局异常的方法。
@ModelAttribute用于thymeleaf、Velocity、FreeMarker等模板引擎数据共享
@SessionAttributes用于将数据存储到session中
配置 注解
@Configuration用于声明一个Java形式的配置类,SpringBoot推荐使用Java配置,在该类中声明的Bean等配置将被SpringBoot的组件扫描功能扫描到。
@Bean用于修饰方法,标识该方法会创建一个Bean实例,并交给Spring容器来管理。
@Value用于注入在配置文件中配置好的属性
@EnableConfigurationProperties,启动@ConfigurationProperties功能
@ConfigurationProperties用于批量注入外部配置,以对象的形式来导入指定前缀的配置
@SpringBootApplication用于表示SpringBoot应用中的启动类,相当于@EnableAutoConfiguration、@EnableAutoConfiguration和@ComponentScan三个注解的结合体。
@EnableAutoConfiguration启用SpringBoot的自动化配置,会根据你在pom.xml添加的依赖和application-dev.yml中的配置自动创建你需要的配置。
@ComponentScan启用SpringBoot的组件扫描功能,将自动装配和注入指定包下的Bean实例。
@CrossOrigin支持跨域请求
事务 注解
@EnableTransactionManagement启用Spring基于注解的事务管理功能,需要和@Configuration注解一起使用。
@Transactional表示方法和类需要开启事务,当作用与类上时,类中所有方法均会开启事务,当作用于方法上时,方法开启事务,方法上的注解无法被子类所继承。
Spring定义了七种传播方式
TransactionDefinition.PROPAGATION_REQUIRED:"如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。"
TransactionDefinition.PROPAGATION_REQUIRES_NEW:"创建一个新的事务,如果当前存在事务,则把当前事务挂起。"
TransactionDefinition.PROPAGATION_SUPPORTS:"如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。"
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:"以非事务方式运行,如果当前存在事务,则把当前事务挂起。"
TransactionDefinition.PROPAGATION_NEVER:"以非事务方式运行,如果当前存在事务,则抛出异常。"
TransactionDefinition.PROPAGATION_MANDATORY:"如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。"
TransactionDefinition.PROPAGATION_NESTED:"如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;"、"如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。"
Spring中定义了五种隔离规则
@Transactional(isolation = Isolation.DEFAULT)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Transactional(isolation = Isolation.READ_COMMITTED)
@Transactional(isolation = Isolation.REPEATABLE_READ)
@Transactional(isolation = Isolation.SERIALIZABLE)
回滚规则
@Transactional(rollbackFor = Exception.class)
事务超时
@Transactional(timeout=30)
是否只读
@Transactional(readOnly = true)
事务失效
@Transactional注解未打在public方法上
目标方法用final修饰
同一个类中的方法直接内部调用原因:方法被事务管理是因为Apring AOP为其生成代理了对象,但是直接this调用同类方
缓存 注解
@EnableCaching开启缓存配置,支持子类代理或者AspectJ增强
@CacheConfig在一个类下,提供公共缓存配置
@Cacheable放着方法和类上,缓存方法或类下所有方法的返回值
@CachePut每次先执行方法,再将结果放入缓存
@CacheEvict删除缓存
@Caching可以配置@Cacheable、@CachePut、@CacheEvict
定时器 注解
@EnableScheduling开启定时任务功能
@Scheduled按指定执行周期执行方法
@Schedules包含多个@Scheduled,可同时运行多个周期配置
@EnableAsync启动异步方法调用的注解,需要将其放置在一个配置类上,并且在配置类中通过 @Bean方法创建一个线程池
@Async异步方法的注解,通过在方法上添加 @Async 注解,可以让该方法在异步线程中执行
测试 注解
@SpringBootTest:用于指定测试类启用Spring Boot Test功能。
@Test:指定方法为测试方法。
@ParameterizedTest:表示方法是参数化测试。
@RepeatedTest:表示方法可重复执行。
@DisplayName:为测试类或者测试方法设置展示名称。
@BeforeEach:表示在每个单元测试之前执行。
@AfterEach:表示在每个单元测试之后执行。
@BeforeAll:表示在所有单元测试之前执行。
@AfterAll:表示在所有单元测试之后执行。
@Tag:表示单元测试类别,类似于JUnit4中的@Categories。
@Disabled:表示测试类或测试方法不执行,类似于JUnit4中的@Ignore。
@Timeout:表示测试方法运行如果超过了指定时间将会返回错误。
@ExtendWith:为测试类或测试方法提供扩展类引用。
选择器 注解
@Conditional用于表示当某个条件满足时,该组件或Bean将被Spring容器创建
@ConditionalOnBean:当某个Bean存在时,配置生效。
@ConditionalOnMissingBean:当某个Bean不存在时,配置生效。
@ConditionalOnClass:当某个类在Classpath存在时,配置生效。
@ConditionalOnMissingClass:当某个类在Classpath不存在时,配置生效。
@ConditionalOnCloudPlatform,在指定云平台才注册配置
@ConditionalOnExpression,指定spel为true时注册配置
@ConditionalOnJava,在指定java版本时注册配置
@ConditionalOnNotWebApplication,不是在web环境才注册配置
@ConditionalOnProperty,配置文件中的值与指定值是否相等,相等才注册配置
@ConditionalOnResource,指定resources都在classpath上才注册配置
@ConditionalOnSingleCandidate,上下文中只有一个候选者bean时才注册配置
@ConditionalOnWebApplication,是在web环境才注册配置
其它 注解
@ImportAutoConfiguration导入配置类,一般做测试的时候使用,正常优先使用@EnableAutoConfiguration
@SpringBootConfiguration替代@Configuration
@ImportResource将资源导入容器
@PropertySource导入properties文件
@PropertySources是@PropertySource 的集合
@Lazy使bean懒加载,取消bean预初始化。
@Primary自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否者将抛出异常。
@Profile指定Bean在哪个环境下被激活
@DependsOn依赖的bean注册完成,才注册当前类,依赖bean不存在会报错。用于控制bean加载顺序
@Lookup根据方法返回的类型,去容器中捞出对应
@Required检查bean的属性setXXX()方法,要求属性砸死配置阶段必须已配置
@Description添加bean的文字描述
@EnableAspectConfiguration启动AspectJ自动配置
@EnableLoadTimeWeaving启动类加载器动态增强功能,使用instrumentation实现
@AutoConfigurationPackage包含该注解的package会被AutoConfigurationPackages注册
@AutoConfigureBefore在指定配置类初始化前加载
@AutoConfigureAfter在指定配置类初始化后加载
@AutoConfigureOrder指定配置类初始化顺序,越小初始化越早
(2)Mybatis框架
SQL语句映射注解
1. @Insert:实现新增功能
@Insert("insert into user(id,name) values(#{id},#{name})")
public int insert(User user);
2. @Update注解:实现更新功能
@Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
void updateUserById(User user);
3. @Delete注解:实现删除功能
@Delete("delete from user where id =#{id}")
void deleteById(Integer id);
4. @Select注解:实现查询功能
@Select("Select * from user")
List<User> queryAllUser();
结果集映射注解
1. @Result注解。
2. @Results注解。
3. @ResultMap是结果集映射的三大注解。
@Select({"select id, name, class_id from student"})
@Results(id="studentMap", value={
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
@Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAll();
@Select({"select id, name, class_id from student where id = #{id}"})
@ResultMap(value="studentMap")
Student selectById(integer id);
关系映射注解
1. @one注解:用于一对一关系映射
@Select("select * from student")
@Results({
@Result(id=true,property="id",column="id"),
@Result(property="name",column="name"),
@Result(property="age",column="age"),
@Result(property="address",column="address_id",one=@One(select="cn.mappers.AddressMapper.getAddress"))
})
public List<Student> getAllStudents();
2. @many注解:用于一对多关系映射
@Select("select * from t_class where id=#{id}")
@Results({
@Result(id=true,column="id",property="id"),
@Result(column="class_name",property="className"),
@Result(property="students", column="id", many=@Many(select="cn.mappers.StudentMapper.getStudentsByClassId"))
})
public Class getClass(int id);
SQL定义标签
1. select用于数据查询操作
<select id="selectUserInfo" parameterType="int" resultType="map">
select * from user_info where id=#{keyId}
</select>
2. insert用于数据保存操作
<insert id="insertUserInfo" parameterType="map" useGeneratedKeys="true" keyProperty="keyId">
insert into user_info (
userName,
userSex
)values(
#{userName},
#{userSex}
)
</insert>
3. update用于数据更新操作
<update id="updateUserInfo" parameterType="map">
update user_info
set userName=#{userName}
where id=#{keyId}
</update>
4. delete用于数据删除操作
<delete id="selectUserInfo" parameterType="int">
delete from user_info
where id=#{keyId}
</delete>
5. resultMap是SQL返回与实体类映射关系信息
<resultMap id="userInfoMap" type="User">
<result property="user_name" column="userName"/>
<result property="user_sex" column="userSex"/>
</resultMap>
<select id="selectUserInfo" parameterType="int" resultType="userInfoMap">
select
userName,
userSex
from user_info
where id=#{keyId}
</select>
6. sql用于定义可重用的 SQL 代码片段,以便在多个SQL语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。
<!-- 定义 -->
<sql id="userColumns"> ${alias}.userName,${alias}.userSex</sql>
<!-- 运用 -->
<select id="selectUserInfo" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from user_info t1
left join user_info_copy t2
</select>
SQL动态标签
1. if单个条件判断,用以实现条件筛选
<select id="selectUserInfo" parameterType="map" resultType="map">
select * from user_info
where 1=1
<if test="userSex !=null and userSex !='' ">
and userSex=#{userSex}
</if>
<if test="userName !=null and userName !='' ">
and userName like CONCAT('%',#{userName},'%')
</if>
</select>
2. foreach用于更新或保存数据时的批量操作
<!-- userList为List<HashMap<String,Object>>类型数据 -->
insert into user_info(
userName,
userSex
)values
<foreach item="item" index="index" collection="userList" separator="," >
(
#{item.userName},
#{item.userSex}
)
</foreach>
<!-- userList为List<String>类型数据 -->
insert into user_info(
userName
)values
<foreach item="item" index="index" collection="userList" separator="," >
(
#{userName}
)
</foreach>
update user_info
set userAge=#{userAge}
where id in
<foreach collection="keyIds" index="index" item="item" separator="," open="(" close=")">
#{item}
</foreach>
3. choose/when/otherwise用以实现条件的多种判断,类似与if else
<select id="selectUserInfo" parameterType="map" resultType="map">
select * from user_info
where 1=1
<choose>
<when test="userFlag!=null and userFlag!='' and userFlag=='Y'">
and id<=100
</when>
<when test="userFlag!=null and userFlag!='' and userFlag=='N'">
and id <=200
</when>
<otherwise>
and id<=300
</otherwise>
</choose>
</select>
4. where只会在子元素返回任何内容的情况下才插入 “WHERE” 子句,并且可以自动处理判断条件语句返回的第一个and或or
<select id="selectUserInfo" parameterType="map" resultType="map">
select * from user_info
<where>
<if test="userSex !=null and userSex !='' ">
userSex=#{userSex}
</if>
<if test="userName !=null and userName !='' ">
and userName like CONCAT('%',#{userName},'%')
</if>
</where>
</select>
5. set可以动态更新需要更新的列,忽略其它不更新的列
<update id="updateUserInfo" parameterType="map">
update user_info
<set>
<if test="userName!= null and userName!=''">
userName=#{userName},
</if>
userSex=#{userSex}
</set>
where id=#{keyId}
</update>
(3)Mybatis-Plus框架
Service CRUD 接口
说明:
通用 Service CRUD 封装IService (opens new window)接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
泛型 T 为任意实体对象
建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
对象 Wrapper 为 条件构造器
Save
// 插入一条记录(选择字段,策略插入)
boolean save(T entity);
// 插入(批量)
boolean saveBatch(Collection<T> entityList);
// 插入(批量)
boolean saveBatch(Collection<T> entityList, int batchSize);
SaveOrUpdate
// TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
Remove
// 根据 queryWrapper 设置的条件,删除记录
boolean remove(Wrapper<T> queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件,删除记录
boolean removeByMap(Map<String, Object> columnMap);
// 删除(根据ID 批量删除)
boolean removeByIds(Collection<? extends Serializable> idList);
Update
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根据 whereWrapper 条件,更新记录
boolean update(T updateEntity, Wrapper<T> whereWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);
Get
// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
// 根据 Wrapper,查询一条记录
Map<String, Object> getMap(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
List
// 查询所有
List<T> list();
// 查询列表
List<T> list(Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// 查询(根据 columnMap 条件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查询所有列表
List<Map<String, Object>> listMaps();
// 查询列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查询全部记录
List<Object> listObjs();
// 查询全部记录
<V> List<V> listObjs(Function<? super Object, V> mapper);
// 根据 Wrapper 条件,查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
Page
// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
Count
// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper<T> queryWrapper);
Chain
query
// 链式查询 普通
QueryChainWrapper<T> query();
// 链式查询 lambda 式。注意:不支持 Kotlin
LambdaQueryChainWrapper<T> lambdaQuery();
// 示例:
query().eq("column", value).one();
lambdaQuery().eq(Entity::getId, value).list();
update
// 链式更改 普通
UpdateChainWrapper<T> update();
// 链式更改 lambda 式。注意:不支持 Kotlin
LambdaUpdateChainWrapper<T> lambdaUpdate();
// 示例:
update().eq("column", value).remove();
lambdaUpdate().eq(Entity::getId, value).update(entity);
Mapper CRUD 接口
说明:
通用 CRUD 封装BaseMapper (opens new window)接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
泛型 T 为任意实体对象
参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键
对象 Wrapper 为 条件构造器
Insert
// 插入一条记录
int insert(T entity);
Delete
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
Update
// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);
Select
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
group
// 查询表内记录,封装返回为Map<属性,List<实体>>
Map<K, List<T>> group(LambdaQueryWrapper<T> wrapper, SFunction<T, A> sFunction, Consumer<T>... peeks);
// 查询表内记录,封装返回为Map<属性,List<实体>>,考虑了并行流的情况
Map<K, List<T>> group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, boolean isParallel, Consumer<T>... peeks);
// 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器>
M group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, Consumer<T>... peeks);
// 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器>,考虑了并行流的情况
M group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, boolean isParallel, Consumer<T>... peeks);
list
// 查询表内记录,封装返回为List<属性>
List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks);
// 查询表内记录,封装返回为List<属性>,考虑了并行流的情况
List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks);
条件构造器
AbstractWrapper
说明:
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
说明:
以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age)
以下代码块内的多个方法均为从上往下补全个别boolean类型的入参,默认为true
以下出现的泛型Param均为Wrapper的子类实例(均具有AbstractWrapper的所有方法)
以下方法在入参中出现的R为泛型,在普通wrapper中是String,在LambdaWrapper中是函数(例:Entity::getId,Entity为实体类,getId为字段id的getter Method)
以下方法入参中的R column均表示数据库字段,当R具体类型为String时则为数据库字段名(字段名是数据库关键字的自己用转义符包裹!)!而不是实体类数据字段名!!!,另当R具体类型为SFunction时项目runtime不支持eclipse自家的编译器!!!
以下举例均为使用普通wrapper,入参为Map和List的均以json形式表现!
使用中如果入参的Map或者List为空,则不会加入最后生成的sql中!!!
#eq
eq(R column, Object val)
eq(boolean condition, R column, Object val)
等于 =
例: eq("name", "老王")--->name = '老王'
#ne
ne(R column, Object val)
ne(boolean condition, R column, Object val)
不等于 <>
例: ne("name", "老王")--->name <> '老王'
#gt
gt(R column, Object val)
gt(boolean condition, R column, Object val)
大于 >
例: gt("age", 18)--->age > 18
#ge
ge(R column, Object val)
ge(boolean condition, R column, Object val)
大于等于 >=
例: ge("age", 18)--->age >= 18
#lt
lt(R column, Object val)
lt(boolean condition, R column, Object val)
小于 <
例: lt("age", 18)--->age < 18
#le
le(R column, Object val)
le(boolean condition, R column, Object val)
小于等于 <=
例: le("age", 18)--->age <= 18
#between
between(R column, Object val1, Object val2)
between(boolean condition, R column, Object val1, Object val2)
BETWEEN 值1 AND 值2
例: between("age", 18, 30)--->age between 18 and 30
#notBetween
notBetween(R column, Object val1, Object val2)
notBetween(boolean condition, R column, Object val1, Object val2)
NOT BETWEEN 值1 AND 值2
例: notBetween("age", 18, 30)--->age not between 18 and 30
#like
like(R column, Object val)
like(boolean condition, R column, Object val)
LIKE '%值%'
例: like("name", "王")--->name like '%王%'
#notLike
notLike(R column, Object val)
notLike(boolean condition, R column, Object val)
NOT LIKE '%值%'
例: notLike("name", "王")--->name not like '%王%'
#likeLeft
likeLeft(R column, Object val)
likeLeft(boolean condition, R column, Object val)
LIKE '%值'
例: likeLeft("name", "王")--->name like '%王'
#likeRight
likeRight(R column, Object val)
likeRight(boolean condition, R column, Object val)
LIKE '值%'
例: likeRight("name", "王")--->name like '王%'
#notLikeLeft
notLikeLeft(R column, Object val)
notLikeLeft(boolean condition, R column, Object val)
NOT LIKE '%值'
例: notLikeLeft("name", "王")--->name not like '%王'
#notLikeRight
notLikeRight(R column, Object val)
notLikeRight(boolean condition, R column, Object val)
NOT LIKE '值%'
例: notLikeRight("name", "王")--->name not like '王%'
#isNull
isNull(R column)
isNull(boolean condition, R column)
字段 IS NULL
例: isNull("name")--->name is null
#isNotNull
isNotNull(R column)
isNotNull(boolean condition, R column)
字段 IS NOT NULL
例: isNotNull("name")--->name is not null
#in
in(R column, Collection<?> value)
in(boolean condition, R column, Collection<?> value)
字段 IN (value.get(0), value.get(1), ...)
例: in("age",{1,2,3})--->age in (1,2,3)
in(R column, Object... values)
in(boolean condition, R column, Object... values)
字段 IN (v0, v1, ...)
例: in("age", 1, 2, 3)--->age in (1,2,3)
#notIn
notIn(R column, Collection<?> value)
notIn(boolean condition, R column, Collection<?> value)
字段 NOT IN (value.get(0), value.get(1), ...)
例: notIn("age",{1,2,3})--->age not in (1,2,3)
notIn(R column, Object... values)
notIn(boolean condition, R column, Object... values)
字段 NOT IN (v0, v1, ...)
例: notIn("age", 1, 2, 3)--->age not in (1,2,3)
#inSql
inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)
字段 IN ( sql语句 )
例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)
例: inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)
#notInSql
notInSql(R column, String inValue)
notInSql(boolean condition, R column, String inValue)
字段 NOT IN ( sql语句 )
例: notInSql("age", "1,2,3,4,5,6")--->age not in (1,2,3,4,5,6)
例: notInSql("id", "select id from table where id < 3")--->id not in (select id from table where id < 3)
#groupBy
groupBy(R... columns)
groupBy(boolean condition, R... columns)
分组:GROUP BY 字段, ...
例: groupBy("id", "name")--->group by id,name
#orderByAsc
orderByAsc(R... columns)
orderByAsc(boolean condition, R... columns)
排序:ORDER BY 字段, ... ASC
例: orderByAsc("id", "name")--->order by id ASC,name ASC
#orderByDesc
orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
排序:ORDER BY 字段, ... DESC
例: orderByDesc("id", "name")--->order by id DESC,name DESC
#orderBy
orderBy(boolean condition, boolean isAsc, R... columns)
排序:ORDER BY 字段, ...
例: orderBy(true, true, "id", "name")--->order by id ASC,name ASC
#having
having(String sqlHaving, Object... params)
having(boolean condition, String sqlHaving, Object... params)
HAVING ( sql语句 )
例: having("sum(age) > 10")--->having sum(age) > 10
例: having("sum(age) > {0}", 11)--->having sum(age) > 11
#or
or()
or(boolean condition)
拼接 OR
注意事项:
主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)
例: eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'
or(Consumer<Param> consumer)
or(boolean condition, Consumer<Param> consumer)
OR 嵌套
例: or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')
#and
and(Consumer<Param> consumer)
and(boolean condition, Consumer<Param> consumer)
AND 嵌套
例: and(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')
#exists
exists(String existsSql)
exists(boolean condition, String existsSql)
拼接 EXISTS ( sql语句 )
例: exists("select id from table where age = 1")--->exists (select id from table where age = 1)
#notExists
notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)
拼接 NOT EXISTS ( sql语句 )
例: notExists("select id from table where age = 1")--->not exists (select id from table where age = 1)
lambda 编程进阶
#QueryWrapper
public void select1(){
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("id","name","nickname");
List<User> users = userMapper.selectList(qw);
users.forEach(System.out::println);
}
public void select2(){
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select(User.class,i -> i.getColumn().equals("name"));//查询name
// qw.select(User.class,i -> !i.getColumn().equals("nickname")); //排除nickname
List<User> users = userMapper.selectList(qw);
users.forEach(System.out::println);
}
#UpdateWrapper
public void set(){
UpdateWrapper<User> uw = new UpdateWrapper<>();
uw.set("gender",false)
.in("id",1,3,5);
int res = userMapper.update(null, uw);
System.out.println(res);
}
public void setSQL(){
UpdateWrapper<User> uw = Wrappers.update();
uw.setSql("password=4321"); // UPDATE tb_user SET password=4321;
int res = userMapper.update(null, uw);
System.out.println(res);
}
#QueryWrapper # lambda
public void between(){
QueryWrapper<User> qw = Wrappers.query();
qw.lambda().between(User::getId,10,20);
List<User> users = userMapper.selectList(qw);
users.forEach(System.out::println);
}
public void delete(){
QueryWrapper<User> qw = Wrappers.query();
qw.lambda().eq(User::getId,30);
int res = userMapper.delete(qw);
}
public void update(){
UpdateWrapper<User> uw = Wrappers.update();
uw.lambda().eq(User::getId,30).setSql("name='张三四'");
int res = userMapper.update(null, uw);
System.out.println(res);
}
public void count(){
QueryWrapper<User> qw = Wrappers.query();
qw.lambda().like(User::getNickname,"an");
Integer res = userMapper.selectCount(qw);
System.out.println(res);
}
#LambdaQueryWrapper
public void eqDemo() {
//使用 QueryWrapper 的成员方法方法 lambda 构建 LambdaQueryWrapper
LambdaQueryWrapper<User> lqw = new QueryWrapper<User>().lambda();
lqw.eq(User::getId, 2);
User user = userMapper.selectOne(lqw);
System.out.println(user);
}
public void inDemo() {
//直接 new 出 LambdaQueryWrapper
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.in(User::getId, 1, 2, 3);
List<User> users = userMapper.selectList(lqw);
users.forEach(System.out::println);
}
public void likeDemo() {
//使用 Wrappers 的静态方法 lambdaQuery 构建 LambdaQueryWrapper(推荐)
LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
lqw.like(User::getUsername, "an");
List<User> users = userMapper.selectList(lqw);
users.forEach(System.out::println);
}
public void oneDemo() {
//使用 链式查询 的方法构建 LambdaQueryWrapper
User user = new LambdaQueryChainWrapper<User>(userMapper)
.eq(User::getId, 10)
.one();
System.out.println(user);
}
public void likeRightDemo() {
//使用 链式查询 的方法构建 LambdaQueryWrapper
List<User> users = new LambdaQueryChainWrapper<User>(userMapper)
//相当于原生 sql 的 like 右通配符模糊查询,比如:like 'an%'
.likeRight(User::getUsername, "an")
.list();
users.forEach(System.out::println);
}
public void likeLeftDemo() {
LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
//相当于原生 sql 的 like 右通配符模糊查询,比如:like '%an'
lqw.likeLeft(User::getUsername, "an");
List<User> users = userMapper.selectList(lqw);
users.forEach(System.out::println);
}
public void partColumnsDemo() {
LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
//查询指定的字段
lqw.select(User::getId, User::getName, User::getNickname)
.eq(User::getUsername, "an");
List<User> users = userMapper.selectList(lqw);
users.forEach(System.out::println);
}
public void ifDemo() {
User condition = User.builder().username("zhangsan").build();
LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
lqw.eq(condition.getGender() != null, User::getGender, condition.getGender())
.eq(condition.getUsername() != null, User::getUsername, condition.getUsername());
List<User> users = userMapper.selectList(lqw);
users.forEach(System.out::println);
}
public void orAndDemo() {
LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
lqw.gt(User::getCredit, 10000)
.and(wrapper -> wrapper.lt(User::getCredit, 100000))
.or()
.eq(User::getGender, true);
List<User> users = userMapper.selectList(lqw);
users.forEach(System.out::println);
}
public void pageDemo() {
Page<User> page = new Page<>(4, 3);
LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(User::getId);
Page<User> res = userMapper.selectPage(page, lqw);
res.getRecords().forEach(System.out::println);
}
public void updateDemo1() {
LambdaUpdateWrapper<User> luw = Wrappers.lambdaUpdate();
luw.eq(User::getId, 5)
.set(User::getName, "大中国")
.set(User::getUsername, "老大");
userMapper.update(null, luw);
}
public void updateDemo2() {
LambdaUpdateWrapper<User> luw = new LambdaUpdateWrapper<>();
luw.eq(User::getId, 3)
.set(User::getNickname, "wanger");
int res = userMapper.update(null, luw);
System.out.println(res);
}
public void updateDemo3() {
boolean res = new LambdaUpdateChainWrapper<User>(userMapper)
.eq(User::getId, 4)
.set(User::getNickname, "mazi")
.update();
System.out.println(res);
}
逻辑删除
步骤 1: 配置application.yml
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
步骤 2: 实体类字段上加上@TableLogic注解
@TableLogic
private Integer deleted;
自动填充功能
public class User {
// 注意!这里需要标记为填充字段
@TableField(.. fill = FieldFill.INSERT)
private String fillField;
....
}
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
// 或者
this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
// 或者
this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
// 或者
this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
// 或者
this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
}
}
多数据源
1、引入dynamic-datasource-spring-boot-starter。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
2、配置数据源。
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master:
url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
slave_1:
url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave_2:
url: ENC(xxxxx) # 内置加密,使用请查看详细文档
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.jdbc.Driver
#......省略
#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
3、使用 @DS 切换数据源。
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
@Service
@DS("slave")
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public List selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DS("slave_1")
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}
乐观锁
说明:
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1
newVersion 会回写到 entity 中
仅支持 updateById(id) 与 update(entity, wrapper) 方法
在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
// Spring Boot 方式
@Configuration
@MapperScan("按需修改")
public class MybatisPlusConfig {
/**
* 旧版
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 新版
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
@Version
private Integer version;
(4)JPA框架
配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
#MySql8.0
spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useSSL=false&characterEncoding=utf-8&useUnicode=true&serverTimezone=Asia/Shanghai
username: root
password: root
#JPA配置
jpa:
hibernate:
#更新或创建表结构
ddl-auto: update
#控制台显示sql
show-sql: true
实体类
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
@Entity
@Table(name = "person")
@Setter
@Getter
@ToString
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "age")
private String age;
}
数据层
import com.zhaoyang.springbootjpa.bean.Person;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PersonRepository extends CrudRepository<Person,Integer> {
}
控制层
import com.zhaoyang.springbootjpa.bean.Person;
import com.zhaoyang.springbootjpa.dao.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
@RestController
@RequestMapping("/jpa")
public class PersonController {
@Autowired
private PersonRepository tUserRepository;
@RequestMapping("/m1")
public String m1(Person person) {
tUserRepository.save(person);
return "SUCCESS";
}
@RequestMapping("/m2")
public String m2(Integer id) {
tUserRepository.deleteById(id);
return "SUCCESS";
}
@RequestMapping("/m3")
public Optional<Person> m3(Integer id) {
return tUserRepository.findById(id);
}
}
JPA和MP区别
JPA是官方推出的Java持久层操作标准(现主要使用Hibernate实现),使用SpringData技术和JpaRepository接口技术,也可以达到简化数据层的目的。要在SpringBoot中使用SpringDataJPA,需要spring-boot-starter-data-jpa依赖库的支持。
mybatis支持由mybatis己实现。JPA是Java的标准。
在使用Spring整合ORM组件的过程中,为了达到简化的目的,往往会进行大量的配置。利用SpringBoot可以进一步实现配置的简化。SpringBoot整合MyBatis开发框架,MyBatis是一款常用并且配置极为简单的ORM开发框架。
国内的话一般Mybatis份额比较多,大部分原因是国内BAT导致的,因为早期阿里采用的是iBatis,优点:简单、学习成本低、易优化等等。老外喜欢JPA是因为OOP、DDD,他们认为写 SQL 不优雅。
相同点
1:都包装了简单的CRUD,可以直接用
2:都支持自定义sql(如:@Select())
不同点
一、从实现来说:CURD实现方式不一样
mybatis-plus :
Mapper(Dao)类extends BaseMapper
实现类里extends ServiceImpl
接口类extends IService
jpa:
repository类(等同于Mapper/DAO类)extends JpaRepository
但是,jpa在定义好数据库类(如:User类)后,需要compile一下(会生产QUser类)
二、分页上
mybatis-plus :
有自己的分页插件,也能很好的支持第三方插件,如:pageHelper
jpa:需要自己处理count查询
三、雪花id
mybatis-plus: 自带
jpa:需要自己实现
四、伪删除
mybatis-plus: 有@TableLogic
jpa:需要自己实现
jpa:
1:默认使用hibernate作为ORM
2:更加偏向于面向对象
3:支持多表关联(用JPAQuery,不是像mybatis那样写在xml)
mybatis:
1:避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
2:更偏向于面向数据(面向关系),致命优点:简单、可读性强
性能:
两个ORM框架的效率不会查特别多(毕竟一次插入万级数据,且要求ms级别,这种毕竟是极少数)。平时开发中,几百条数据的情况下,两者都差不多的。
到底用哪个
第一:跟着公司走,毕竟大头兵没啥发言权
第二:JPA的学习成本比MyBatis-plus大