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的注解和反射到时我会抽时间写一个系列与大家分享

原文地址:https://www.cnblogs.com/youcong/p/9026572.html

时间: 2024-10-11 10:24:34

MP实战系列(四)之DAO讲解的相关文章

MP实战系列(七)之集成springboot

springboot是现在比较流行的微服使用的框架,springboot本质上就是将spring+springmvc+mybatis零配置化,基本上springboot的默认配置符合我们的开发.当然有一部分还是需要自定义的. 本章不是专门讲springboot的,主要将springboot+mybatis plus是如何整合的. 一.导入pom依赖 <parent> <groupId>org.springframework.boot</groupId> <arti

MP实战系列(十二)之封装方法详解(续二)

继续MP实战系列(十一)之封装方法详解(续一)这篇文章之后. 此次要讲的是关于查询. 查询是用的比较多的,查询很重要,好的查询,加上索引如鱼得水,不好的查询加再多索引也是无济于事. 1.selectById()方法 演示示例: UserEntity user = ud.selectById(33); System.out.println(user.getEmail()); 简单的说明: 如果是在MyBatis中,需要再对应的xml编写这样的sql select column1,column2..

MP实战系列(九)之集成Shiro

下面示例是在之前的基础上进行的,大家如果有什么不明白的可以参考MP实战系列的前八章 当然,同时也可以参考MyBatis Plus官方教程 建议如果参考如下教程,使用的技术为spring+mybatis plus + springmvc+jdk8+maven工程 满足这个条件可以减少不必要的麻烦,当然持久层也可以用mybatis. 只要按照如下示例来,也不会有大问题的.之前我也强调过mybatis和mybatis plus的区别主要是封装和继承,mybatis plus封装一系列增删改查的方法,但

MP实战系列(二)之集成swagger

其实与spring+springmvc+mybatis集成swagger没什么区别,只是之前写的太不好了,所以这次决定详细写. 提到swagger不得不提rest,rest是一种架构风格,里面有对不同的资源有不同的请求标识.例如PUT,POST,GET,DELETE,OPTIONS,HEAD,PATCH等. 对于技术的初学,最好的话还是建议去官网,官网最详细也最权威,虽然不少博客对此有挺好的解说,但还是强烈建议去官网,不要求仔仔细细阅读,至少读个大概. 对于目前,有人要问我swagger能做什么

MP实战系列(三)之实体类讲解

首先说一句,mybatis plus实在太好用了! mybaits plus的实体类: 以我博客的用户类作为讲解 package com.blog.entity; import com.baomidou.mybatisplus.annotations.TableField; import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.annotations.TableLogic; imp

ElasticSearch实战系列四: ElasticSearch理论知识介绍

前言 在前几篇关于ElasticSearch的文章中,简单的讲了下有关ElasticSearch的一些使用,这篇文章讲一下有关 ElasticSearch的一些理论知识以及自己的一些见解. 虽然本人是一个实战派,不太喜欢讲这些理论知识,因为这块可以查看官方文档,那里会写得非常详细,但是在使用了ElasticSearch之后,发现有的知识点需要掌握一定的理论知识才能理解,对于初学者来说有的不好理解,因此写下该篇文章,希望读者在看完之后能够有所帮助. ElasticSearch 理论知识介绍 Ela

MP实战系列(五)之封装方法讲解

mybatis plus封装的方法怎么用?以及它们对应的sql是那些sql?及其什么情况用? 这些需要说下,以下我将会将我常用的说下,不是常用的,可能提以下或者不提. 根据主键查询 UserEntity userEntity = ud.selectById(id); 上述这个没什么好说的 根据实体查询 UserEntity u = new UserEntity(); u.setEmail("[email protected]"); UserEntity u1 = ud.selectOn

MP实战系列(十三)之批量修改操作(前后台异步交互)

MyBatis的批量操作其实同MyBatis基本是一样的.并无多大区别,要说区别,除了封装的方法之外,主要就是注解方面的区别,比如@TableId.@TableField.@TableName等等区别. 示例描述: 本次描述的是批量相关的操作,主要是批量修改等操作. 项目讲解:如何批量修改开锁方式? 准备环境和IDE工具:MySQL5.7+Maven3以上+JDK8或者JDK7+SSM框架+tomcat8或者tomcat7应用服务器+Eclipse. 本文核心:主要是Controller代码和数

shiro实战系列(四)之配置

Shiro之配置 Shiro 被设计成能够在任何环境下工作,从最简单的命令行应用程序到最大的的企业群集应用.由于环境的多样性,使得许多配置机制适用于它的配置. 一. 许多配置选项 Shiro的SecurityManager实现及所支持的组件都是兼容JavaBean的.这使得Shiro几乎能使用任何配置格式,如regular Java,XML(Spring, JBoss, Guice,等等),YAML,JSON,Groovy Builder markup,以及更多的配置. 二. 可编程配置 创建一