Springboot三层架构--DAO层Service层Colltroler层--这波我在外太空
Posted 什么时候养猫猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springboot三层架构--DAO层Service层Colltroler层--这波我在外太空相关的知识,希望对你有一定的参考价值。
目录
1.DAO层
全称数据访问层,全称data access object,属于一种比较底层,比较基础的操作。具体到对于某个表、某个实体类的增删改查,即用于数据库的增删改查,表达的是对SQL语句的封装,建议对DAO只做原子操作。有多少张表就有多少个DAO层。在mybatis中,方法主要与xxx.xml内一一对应,相互映射。
Dao层的设计
dao层负责与数据库联络的一些任务封装在此,dao层首先设计dao层接口,然后在配置文件中定义此类接口的实体类,然后就可以在模块中调用此接口来进行数据处理。不需要关心此接口的具体实现类,结构清晰。dao层的数据源配置以及数据库连接参数都在配置文件中进行配置。
2.Service层
全称业务逻辑层,在该层进行复杂的业务逻辑处理,且只专注逻辑处理,即对于多个dao层进行封装、处理。其中需要的数据库操作通过DAO层去实现。所以我们再Service层需要事务管理。
业务逻辑,就是对数据库获取的数据进行处理,比如从数据库获取num=10,逻辑操作是+1,那么+1操作由Service处理。
Service层的设计
Service层,负责业务模块的逻辑应用设计。同样是先设置接口,再设计实现类,接着在配置文件中配置其关联。这样我们就能在应用中调用Service接口来进行业务处理。Service层的业务类具体要调用已经定义的dao层接口。
service层=service接口(可以根据业务复杂程度来省略)+service实现类
设计Service层的优点
我们通过将dao层封装成Service层,让Service层去调用dao层的接口,有利于业务逻辑的独立性和重复利用。程序显得非常简介。
Dao与Service的关系
DAO面向表,Service面向业务。后端开发先数据库设计出所有的表,然后每一张表设计出DAO层,然后根据其具体的业务逻辑将DAO层封装成一个Service层,对外提供一个服务。
3.Colltroler层
Collertroler层俗称控制层,负责请求转发,接收页面(前端H5或者App)传过来的参数,并调用Service层中定义的方法进行业务操作,再将处理结果返回前端。
Collertroler层的设计
Colltroler负责具体业务模块流程的控制,在此层需要调用Service层提供的接口来控制业务流程,控制的配置同样在配置文件中,针对具体的业务流程,有不同的控制器。我们的设计过程可以将流程进行抽象归纳,设计出可以重复利用的单元流程模块,这样可以使程序结构更清晰,大大减少代码量。
4.项目中的具体流程
Controller层调用Service层的方法,Service层调用Dao(mapper)层中的方法,其中调用的参数是使用Entity层进行传递的。总的来说这样每层做什么的分类只是为了使业务逻辑更加清晰,写代码更加方便,所以有时候也需要根据具体情况来,但是大体的都是这样处理的,因为它其实就是提供一种规则,让你把相同类型的代码放在一起,这样就形成了层次,从而达到分层解耦、复用、便于测试和维护的目的。
entity实体层(model),存放的实体类,与数据库中的属性保持一致,实现set和get方法。用于各层(DAO,Service、Colltroler)之间对象数据的封装和传递
MP实战系列之DAO讲解
说到DAO不得不提一个开发名词"三层架构",所谓的三层架构是什么呢?简单的可以概括为数据访问层,业务逻辑层,界面层(又称表现层).
这也是我们Java开发常用的手段,经常有人将三层架构和mvc模式混淆,在我看来,三层架构就是三层架构,mvc只是三层架构中的表现层中的架构,相当于在一个比较大的层面,往里面在细分,mvc细分,可分为模型,视图,控制器,在这里模型通常指数据,也可以叫JavaBean,而视图的话,这个视图就是展示给用户看的,通常用于视图的模板可以为jsp,freemarker,beetl,velocity或者是springboot推荐使用的thymeleaf等,当然也可以是纯html,不过需要ajax交互获取后台的数据。控制器的话,让人不禁想起了servlet,控制器的作用就相当于servlet的作用,就是对http请求进行处理。
mvc模式也是现在流行的一种软件设计模式。
三层架构可以用来做什么呢?答可以做很多,最大的作用,用一句官方的话,"高内聚,低耦合",软件开发中的大忌就是高耦合,内聚的意思我不是十分明白,不过耦合还是很让人理解的,耦合度高就是你改完这个代码,另外的也要改,再另外的又要改,对于互联网项目,需求变更是经常的,耦合度高的话,开发项目的效率将会非常低下。
我这个人比较喜欢开场博文说说行内话,现在开始讲讲我们的mybatis plus的dao吧!
首先还是那句话,贴源码分析讲解
/** * Copyright (c) 2011-2020, hubin ([email protected]). * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus.mapper; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.session.RowBounds; /** * <p> * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能 * </p> * <p> * 这个 Mapper 支持 id 泛型 * </p> * * @author hubin * @Date 2016-01-23 */ public interface BaseMapper<T> { /** * <p> * 插入一条记录 * </p> * * @param entity 实体对象 * @return int */ Integer insert(T entity); /** * <p> * 插入一条记录 * </p> * * @param entity 实体对象 * @return int */ Integer insertAllColumn(T entity); /** * <p> * 根据 ID 删除 * </p> * * @param id 主键ID * @return int */ Integer deleteById(Serializable id); /** * <p> * 根据 columnMap 条件,删除记录 * </p> * * @param columnMap 表字段 map 对象 * @return int */ Integer deleteByMap(@Param("cm") Map<String, Object> columnMap); /** * <p> * 根据 entity 条件,删除记录 * </p> * * @param wrapper 实体对象封装操作类(可以为 null) * @return int */ Integer delete(@Param("ew") Wrapper<T> wrapper); /** * <p> * 删除(根据ID 批量删除) * </p> * * @param idList 主键ID列表 * @return int */ Integer deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList); /** * <p> * 根据 ID 修改 * </p> * * @param entity 实体对象 * @return int */ Integer updateById(@Param("et") T entity); /** * <p> * 根据 ID 修改 * </p> * * @param entity 实体对象 * @return int */ Integer updateAllColumnById(@Param("et") T entity); /** * <p> * 根据 whereEntity 条件,更新记录 * </p> * * @param entity 实体对象 * @param wrapper 实体对象封装操作类(可以为 null) * @return */ Integer update(@Param("et") T entity, @Param("ew") Wrapper<T> wrapper); /** * <p> * 根据 ID 查询 * </p> * * @param id 主键ID * @return T */ T selectById(Serializable id); /** * <p> * 查询(根据ID 批量查询) * </p> * * @param idList 主键ID列表 * @return List<T> */ List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList); /** * <p> * 查询(根据 columnMap 条件) * </p> * * @param columnMap 表字段 map 对象 * @return List<T> */ List<T> selectByMap(@Param("cm") Map<String, Object> columnMap); /** * <p> * 根据 entity 条件,查询一条记录 * </p> * * @param entity 实体对象 * @return T */ T selectOne(@Param("ew") T entity); /** * <p> * 根据 Wrapper 条件,查询总记录数 * </p> * * @param wrapper 实体对象 * @return int */ Integer selectCount(@Param("ew") Wrapper<T> wrapper); /** * <p> * 根据 entity 条件,查询全部记录 * </p> * * @param wrapper 实体对象封装操作类(可以为 null) * @return List<T> */ List<T> selectList(@Param("ew") Wrapper<T> wrapper); /** * <p> * 根据 Wrapper 条件,查询全部记录 * </p> * * @param wrapper 实体对象封装操作类(可以为 null) * @return List<T> */ List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> wrapper); /** * <p> * 根据 Wrapper 条件,查询全部记录 * 注意: 只返回第一个字段的值 * </p> * * @param wrapper 实体对象封装操作类(可以为 null) * @return List<Object> */ List<Object> selectObjs(@Param("ew") Wrapper<T> wrapper); /** * <p> * 根据 entity 条件,查询全部记录(并翻页) * </p> * * @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT) * @param wrapper 实体对象封装操作类(可以为 null) * @return List<T> */ List<T> selectPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper); /** * <p> * 根据 Wrapper 条件,查询全部记录(并翻页) * </p> * * @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT) * @param wrapper 实体对象封装操作类 * @return List<Map<String, Object>> */ List<Map<String, Object>> selectMapsPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper); }
在Java中有一句经典的话,一个类只能继承一个类,但可以实现多个接口,同样一个接口可以继承多个接口
在此不得不提到一个常见的面试考题:
接口和抽象类的区别:
相同点:
(1)接口和抽象类中都存在抽象方法(抽象方法,就是只声明但尚未实现的方法称为抽象方法),当然一个前提,该抽象类必须要用abstract修饰符修饰,所以我们可以总结它一个共同点即抽象方法。
(2)不能实例化
不同点:
接口:
(1)接口中所有的方法必须是抽象方法;
(2)接口可以多继承(可一个接口继承多个接口,上述的mybatis plus就是基于这个),同样一个接口也可以实现多个接口,这个例子比比皆是;
(3)接口不能实例化
抽象类:
(1)抽象类中可以存在非抽象方法,同时也可以存在抽象方法;
(2)抽象类只能单继承(所以说一个类只能继承一个父类,所以说java是单根继承的,也可以说它是多继承,因为接口具有多继承性质,通过实现多个接口来完成的)
(3)抽象类中可以有静态属性,也可有非静态属性;
下面附我博客的UserDao继承上述BaseMapper接口
package com.blog.dao; import java.io.Serializable; import com.baomidou.mybatisplus.mapper.BaseMapper; import com.blog.entity.UserEntity; public interface UserDao extends BaseMapper<UserEntity> { }
通过继承,我的UserDao具有增,删,改,查等多个方法;通过继承,我不再需要像原始mybatis那样,每写一个方法需要在xml文件进行相应的配置,否则会报异常
看BaseMapper源码,再结合我的UserDao,发现继承是个很好的东西。假如我有一百个dao,我只需继承BaseMapper,就有了对应的一系列增删改查。那么对于开发效率是有多么大的提高啊!
继承思想是个好玩意!比如在js中,日期函数,常用的邮件,手机号,数字等表单校验,全局获取cookie,清除cookie等我可以将其抽象为一个js文件,然后引用到其他html中,其他html就可以用了。这体现了复用思想,同时也间接性反映了继承。
再贴贴Service代码,也是一样的原理
package com.blog.service; import java.util.Map; import com.baomidou.mybatisplus.service.IService; import com.blog.entity.UserEntity; import com.blog.utils.PageUtils; public interface UserService extends IService<UserEntity> { }
IService源码:
/** * Copyright (c) 2011-2016, hubin ([email protected]). * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus.service; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; import com.baomidou.mybatisplus.mapper.Wrapper; import com.baomidou.mybatisplus.plugins.Page; /** * <p> * 顶级 Service * </p> * * @author hubin * @Date 2016-04-20 */ public interface IService<T> { /** * <p> * 插入一条记录(选择字段,策略插入) * </p> * * @param entity 实体对象 * @return boolean */ boolean insert(T entity); /** * <p> * 插入一条记录(全部字段) * </p> * * @param entity 实体对象 * @return boolean */ boolean insertAllColumn(T entity); /** * <p> * 插入(批量),该方法不适合 Oracle * </p> * * @param entityList 实体对象列表 * @return boolean */ boolean insertBatch(List<T> entityList); /** * <p> * 插入(批量) * </p> * * @param entityList 实体对象列表 * @param batchSize 插入批次数量 * @return boolean */ boolean insertBatch(List<T> entityList, int batchSize); /** * <p> * 批量修改插入 * </p> * * @param entityList 实体对象列表 * @return boolean */ boolean insertOrUpdateBatch(List<T> entityList); /** * <p> * 批量修改插入 * </p> * * @param entityList 实体对象列表 * @param batchSize * @return boolean */ boolean insertOrUpdateBatch(List<T> entityList, int batchSize); /** * <p> * 批量修改或插入全部字段 * </p> * * @param entityList 实体对象列表 * @return boolean */ boolean insertOrUpdateAllColumnBatch(List<T> entityList); /** * 批量修改或插入全部字段 * * @param entityList 实体对象列表 * @param batchSize * @return boolean */ boolean insertOrUpdateAllColumnBatch(List<T> entityList, int batchSize); /** * <p> * 根据 ID 删除 * </p> * * @param id 主键ID * @return boolean */ boolean deleteById(Serializable id); /** * <p> * 根据 columnMap 条件,删除记录 * </p> * * @param columnMap 表字段 map 对象 * @return boolean */ boolean deleteByMap(Map<String, Object> columnMap); /** * <p> * 根据 entity 条件,删除记录 * </p> * * @param wrapper 实体包装类 {@link Wrapper} * @return boolean */ boolean delete(Wrapper<T> wrapper); /** * <p> * 删除(根据ID 批量删除) * </p> * * @param idList 主键ID列表 * @return boolean */ boolean deleteBatchIds(Collection<? extends Serializable> idList); /** * <p> * 根据 ID 选择修改 * </p> * * @param entity 实体对象 * @return boolean */ boolean updateById(T entity); /** * <p> * 根据 ID 修改全部字段 * </p> * * @param entity 实体对象 * @return boolean */ boolean updateAllColumnById(T entity); /** * <p> * 根据 whereEntity 条件,更新记录 * </p> * * @param entity 实体对象 * @param wrapper 实体包装类 {@link Wrapper} * @return boolean */ boolean update(T entity, Wrapper<T> wrapper); /** * <p> * 根据ID 批量更新 * </p> * * @param entityList 实体对象列表 * @return boolean */ boolean updateBatchById(List<T> entityList); /** * <p> * 根据ID 批量更新 * </p> * * @param entityList 实体对象列表 * @param batchSize 更新批次数量 * @return boolean */ boolean updateBatchById(List<T> entityList, int batchSize); /** * <p> * 根据ID 批量更新全部字段 * </p> * * @param entityList 实体对象列表 * @return boolean */ boolean updateAllColumnBatchById(List<T> entityList); /** * <p> * 根据ID 批量更新全部字段 * </p> * * @param entityList 实体对象列表 * @param batchSize 更新批次数量 * @return boolean */ boolean updateAllColumnBatchById(List<T> entityList, int batchSize); /** * <p> * TableId 注解存在更新记录,否插入一条记录 * </p> * * @param entity 实体对象 * @return boolean */ boolean insertOrUpdate(T entity); /** * 插入或修改一条记录的全部字段 * * @param entity 实体对象 * @return boolean */ boolean insertOrUpdateAllColumn(T entity); /** * <p> * 根据 ID 查询 * </p> * * @param id 主键ID * @return T */ T selectById(Serializable id); /** * <p> * 查询(根据ID 批量查询) * </p> * * @param idList 主键ID列表 * @return List<T> */ List<T> selectBatchIds(Collection<? extends Serializable> idList); /** * <p> * 查询(根据 columnMap 条件) * </p> * * @param columnMap 表字段 map 对象 * @return List<T> */ List<T> selectByMap(Map<String, Object> columnMap); /** * <p> * 根据 Wrapper,查询一条记录 * </p> * * @param wrapper 实体对象 * @return T */ T selectOne(Wrapper<T> wrapper); /** * <p> * 根据 Wrapper,查询一条记录 * </p> * * @param wrapper {@link Wrapper} * @return Map<String,Object> */ Map<String, Object> selectMap(Wrapper<T> wrapper); /** * <p> * 根据 Wrapper,查询一条记录 * </p> * * @param wrapper {@link Wrapper} * @return Object */ Object selectObj(Wrapper<T> wrapper); /** * <p> * 根据 Wrapper 条件,查询总记录数 * </p> * * @param wrapper 实体对象 * @return int */ int selectCount(Wrapper<T> wrapper); /** * <p> * 查询列表 * </p> * * @param wrapper 实体包装类 {@link Wrapper} * @return */ List<T> selectList(Wrapper<T> wrapper); /** * <p> * 翻页查询 * </p> * * @param page 翻页对象 * @return */ Page<T> selectPage(Page<T> page); /** * <p> * 查询列表 * </p> * * @param wrapper {@link Wrapper} * @return */ List<Map<String, Object>> selectMaps(Wrapper<T> wrapper); /** * <p> * 根据 Wrapper 条件,查询全部记录 * </p> * * @param wrapper 实体对象封装操作类(可以为 null) * @return List<Object> */ List<Object> selectObjs(Wrapper<T> wrapper); /** * <p> * 翻页查询 * </p> * * @param page 翻页对象 * @param wrapper {@link Wrapper} * @return */ @SuppressWarnings("rawtypes") Page<Map<String, Object>> selectMapsPage(Page page, Wrapper<T> wrapper); /** * <p> * 翻页查询 * </p> * * @param page 翻页对象 * @param wrapper 实体包装类 {@link Wrapper} * @return */ Page<T> selectPage(Page<T> page, Wrapper<T> wrapper); }
其实和Dao也是一样的
再贴贴Service实现类相关代码:
package com.blog.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.service.impl.ServiceImpl; import com.blog.dao.UserDao; import com.blog.entity.UserEntity; import com.blog.service.UserService; @Service("userService") public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService { }
ServiceImpl源码:
/** * Copyright (c) 2011-2016, hubin ([email protected]). * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus.service.impl; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.ibatis.binding.MapperMethod; import org.apache.ibatis.session.SqlSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import com.baomidou.mybatisplus.entity.TableInfo; import com.baomidou.mybatisplus.enums.SqlMethod; import com.baomidou.mybatisplus.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.mapper.BaseMapper; import com.baomidou.mybatisplus.mapper.Condition; import com.baomidou.mybatisplus.mapper.SqlHelper; import com.baomidou.mybatisplus.mapper.Wrapper; import com.baomidou.mybatisplus.plugins.Page; import com.baomidou.mybatisplus.service.IService; import com.baomidou.mybatisplus.toolkit.CollectionUtils; import com.baomidou.mybatisplus.toolkit.MapUtils; import com.baomidou.mybatisplus.toolkit.ReflectionKit; import com.baomidou.mybatisplus.toolkit.StringUtils; import com.baomidou.mybatisplus.toolkit.TableInfoHelper; /** * <p> * IService 实现类( 泛型:M 是 mapper 对象,T 是实体 , PK 是主键泛型 ) * </p> * * @author hubin * @Date 2016-04-20 */ public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> { @Autowired protected M baseMapper; /** * <p> * 判断数据库操作是否成功 * </p> * <p> * 注意!! 该方法为 Integer 判断,不可传入 int 基本类型 * </p> * * @param result 数据库操作返回影响条数 * @return boolean */ protected static boolean retBool(Integer result) { return SqlHelper.retBool(result); } @SuppressWarnings("unchecked") protected Class<T> currentModelClass() { return ReflectionKit.getSuperClassGenricType(getClass(), 1); } /** * <p> * 批量操作 SqlSession * </p> */ protected SqlSession sqlSessionBatch() { return SqlHelper.sqlSessionBatch(currentModelClass()); } /** * 获取SqlStatement * * @param sqlMethod * @return */ protected String sqlStatement(SqlMethod sqlMethod) { return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod()); } @Transactional(rollbackFor = Exception.class) @Override public boolean insert(T entity) { return retBool(baseMapper.insert(entity)); } @Transactional(rollbackFor = Exception.class) @Override public boolean insertAllColumn(T entity) { return retBool(baseMapper.insertAllColumn(entity)); } @Transactional(rollbackFor = Exception.class) @Override public boolean insertBatch(List<T> entityList) { return insertBatch(entityList, 30); } /** * 批量插入 * * @param entityList * @param batchSize * @return */ @Transactional(rollbackFor = Exception.class) @Override public boolean insertBatch(List<T> entityList, int batchSize) { if (CollectionUtils.isEmpty(entityList)) { throw new IllegalArgumentException("Error: entityList must not be empty"); } try (SqlSession batchSqlSession = sqlSessionBatch()) { int size = entityList.size(); String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE); for (int i = 0; i < size; i++) { batchSqlSession.insert(sqlStatement, entityList.get(i)); if (i >= 1 && i % batchSize == 0) { batchSqlSession.flushStatements(); } } batchSqlSession.flushStatements(); } catch (Throwable e) { throw new MybatisPlusException("Error: Cannot execute insertBatch Method. Cause", e); } return true; } /** * <p> * TableId 注解存在更新记录,否插入一条记录 * </p> * * @param entity 实体对象 * @return boolean */ @Transactional(rollbackFor = Exception.class) @Override public boolean insertOrUpdate(T entity) { if (null != entity) { Class<?> cls = entity.getClass(); TableInfo tableInfo = TableInfoHelper.getTableInfo(cls); if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) { Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty()); if (StringUtils.checkValNull(idVal)) { return insert(entity); } else { /* * 更新成功直接返回,失败执行插入逻辑 */ return updateById(entity) || insert(entity); } } else { throw new MybatisPlusException("Error: Can not execute. Could not find @TableId."); } } return false; } @Transactional(rollbackFor = Exception.class) @Override public boolean insertOrUpdateAllColumn(T entity) { if (null != entity) { Class<?> cls = entity.getClass(); TableInfo tableInfo = TableInfoHelper.getTableInfo(cls); if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) { Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty()); if (StringUtils.checkValNull(idVal)) { return insertAllColumn(entity); } else { /* * 更新成功直接返回,失败执行插入逻辑 */ return updateAllColumnById(entity) || insertAllColumn(entity); } } else { throw new MybatisPlusException("Error: Can not execute. Could not find @TableId."); } } return false; } @Transactional(rollbackFor = Exception.class) @Override public boolean insertOrUpdateBatch(List<T> entityList) { return insertOrUpdateBatch(entityList, 30); } @Transactional(rollbackFor = Exception.class) @Override public boolean insertOrUpdateBatch(List<T> entityList, int batchSize) { return insertOrUpdateBatch(entityList, batchSize, true); } @Transactional(rollbackFor = Exception.class) @Override public boolean insertOrUpdateAllColumnBatch(List<T> entityList) { return insertOrUpdateBatch(entityList, 30, false); } @Transactional(rollbackFor = Exception.class) @Override public boolean insertOrUpdateAllColumnBatch(List<T> entityList, int batchSize) { return insertOrUpdateBatch(entityList, batchSize, false); } /** * 批量插入修改 * * @param entityList 实体对象列表 * @param batchSize 批量刷新个数 * @param selective 是否滤掉空字段 * @return boolean */ private boolean insertOrUpdateBatch(List<T> entityList, int batchSize, boolean selective) { if (CollectionUtils.isEmpty(entityList)) { throw new IllegalArgumentException("Error: entityList must not be empty"); } try (SqlSession batchSqlSession = sqlSessionBatch()) { int size = entityList.size(); for (int i = 0; i < size; i++) { if (selective) { insertOrUpdate(entityList.get(i)); } else { insertOrUpdateAllColumn(entityList.get(i)); } if (i >= 1 && i % batchSize == 0) { batchSqlSession.flushStatements(); } } batchSqlSession.flushStatements(); } catch (Throwable e) { throw new MybatisPlusException("Error: Cannot execute insertOrUpdateBatch Method. Cause", e); } return true; } @Transactional(rollbackFor = Exception.class) @Override public boolean deleteById(Serializable id) { return SqlHelper.delBool(baseMapper.deleteById(id)); } @Transactional(rollbackFor = Exception.class) @Override public boolean deleteByMap(Map<String, Object> columnMap) { if (MapUtils.isEmpty(columnMap)) { throw new MybatisPlusException("deleteByMap columnMap is empty."); } return SqlHelper.delBool(baseMapper.deleteByMap(columnMap)); } @Transactional(rollbackFor = Exception.class) @Override public boolean delete(Wrapper<T> wrapper) { return SqlHelper.delBool(baseMapper.delete(wrapper)); } @Transactional(rollbackFor = Exception.class) @Override public boolean deleteBatchIds(Collection<? extends Serializable> idList) { return SqlHelper.delBool(baseMapper.deleteBatchIds(idList)); } @Transactional(rollbackFor = Exception.class) @Override public boolean updateById(T entity) { return retBool(baseMapper.updateById(entity)); } @Transactional(rollbackFor = Exception.class) @Override public boolean updateAllColumnById(T entity) { return retBool(baseMapper.updateAllColumnById(entity)); } @Transactional(rollbackFor = Exception.class) @Override public boolean update(T entity, Wrapper<T> wrapper) { return retBool(baseMapper.update(entity, wrapper)); } @Transactional(rollbackFor = Exception.class) @Override public boolean updateBatchById(List<T> entityList) { return updateBatchById(entityList, 30); } @Transactional(rollbackFor = Exception.class) @Override public boolean updateBatchById(List<T> entityList, int batchSize) { return updateBatchById(entityList, batchSize, true); } @Transactional(rollbackFor = Exception.class) @Override public boolean updateAllColumnBatchById(List<T> entityList) { return updateAllColumnBatchById(entityList, 30); } @Transactional(rollbackFor = Exception.class) @Override public boolean updateAllColumnBatchById(List<T> entityList, int batchSize) { return updateBatchById(entityList, batchSize, false); } /** * 根据主键ID进行批量修改 * * @param entityList 实体对象列表 * @param batchSize 批量刷新个数 * @param selective 是否滤掉空字段 * @return boolean */ private boolean updateBatchById(List<T> entityList, int batchSize, boolean selective) { if (CollectionUtils.isEmpty(entityList)) { throw new IllegalArgumentException("Error: entityList must not be empty"); } try (SqlSession batchSqlSession = sqlSessionBatch()) { int size = entityList.size(); SqlMethod sqlMethod = selective ? SqlMethod.UPDATE_BY_ID : SqlMethod.UPDATE_ALL_COLUMN_BY_ID; String sqlStatement = sqlStatement(sqlMethod); for (int i = 0; i < size; i++) { MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>(); param.put("et", entityList.get(i)); batchSqlSession.update(sqlStatement, param); if (i >= 1 && i % batchSize == 0) { batchSqlSession.flushStatements(); } } batchSqlSession.flushStatements(); } catch (Throwable e) { throw new MybatisPlusException("Error: Cannot execute updateBatchById Method. Cause", e); } return true; } @Override public T selectById(Serializable id) { return baseMapper.selectById(id); } @Override public List<T> selectBatchIds(Collection<? extends Serializable> idList) { return baseMapper.selectBatchIds(idList); } @Override public List<T> selectByMap(Map<String, Object> columnMap) { return baseMapper.selectByMap(columnMap); } @Override public T selectOne(Wrapper<T> wrapper) { return SqlHelper.getObject(baseMapper.selectList(wrapper)); } @Override public Map<String, Object> selectMap(Wrapper<T> wrapper) { return SqlHelper.getObject(baseMapper.selectMaps(wrapper)); } @Override public Object selectObj(Wrapper<T> wrapper) { return SqlHelper.getObject(baseMapper.selectObjs(wrapper)); } @Override public int selectCount(Wrapper<T> wrapper) { return SqlHelper.retCount(baseMapper.selectCount(wrapper)); } @Override public List<T> selectList(Wrapper<T> wrapper) { return baseMapper.selectList(wrapper); } @Override public Page<T> selectPage(Page<T> page) { return selectPage(page, Condition.EMPTY); } @Override public List<Map<String, Object>> selectMaps(Wrapper<T> wrapper) { return baseMapper.selectMaps(wrapper); } @Override public List<Object> selectObjs(Wrapper<T> wrapper) { return baseMapper.selectObjs(wrapper); } @Override public Page<Map<String, Object>> selectMapsPage(Page page, Wrapper<T> wrapper) { SqlHelper.fillWrapper(page, wrapper); page.setRecords(baseMapper.selectMapsPage(page, wrapper)); return page; } @Override public Page<T> selectPage(Page<T> page, Wrapper<T> wrapper) { SqlHelper.fillWrapper(page, wrapper); page.setRecords(baseMapper.selectPage(page, wrapper)); return page; } }
可以发现它们基本是一样的。而在ServiceImpl中可以发现基本方法都加上了事务。
事务又是什么呢?
事务具有四大特性:
(1)原子性
答:原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。
(2)一致性
答:一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。
(3)隔离性
答:隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行
(4)持久性
答:持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
开发中对于对数据库中表数据的改动是十分有必要加上事务的,否则前一个方法执行成功,后一个方法失败,最后数据添加成功,这就尴尬了。就比如取钱,这个例子最经典,比如我有一万存在银行,我最后取出了二万,对我来说发了一笔小财,对于银行而言却损失了一万。对于交易相关的项目加上事务是十分必要的。
最后来一个单元测试:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring/spring.xml") public class JunitTest { @Autowired private UserDao ud; @Test public void testName() throws Exception { UserEntity userEntity = ud.selectById(1); System.out.println(userEntity.getCreateTime()); } }
从这里看,其实与我们原来的单元测试并无区别,因此对于ssm框架用熟练的人,哪怕不是很熟练也可非常容易接受的。
关于mybatis plus的注解和反射到时我会抽时间写一个系列与大家分享
以上是关于Springboot三层架构--DAO层Service层Colltroler层--这波我在外太空的主要内容,如果未能解决你的问题,请参考以下文章