Mybatis & Mybatis-plus 框架基础

Mybatis & Mybatis-plus 框架基础

Mybatis

基本应用

pom.xml

<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatisartifactId>
    <version>3.5.4version>
dependency>
<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>5.1.45version>
dependency>

传统开发方式

定义核心配置文件 SqlMapConfig.xml


<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///bank"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            dataSource>
        environment>
    environments>
    <mappers>
        <mapper resource="UserMapper.xml"/>
    mappers>
configuration>

定义 mapper 文件 UserMapper.xml


<mapper namespace="userMapper">
    <select id="findAll" resultType="com.demo.pojo.User">
        select * from user
    select>
mapper>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
 	private Integer id;
    private String username;
    private String password;
}
public interface UserMapper{
    List<User> findAll();
}
public class UserMapperImpl implements UserMapper {
	@Override
    public List<User> findAll(){
        // 读取核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 生成 sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // userMapper.findAll => namespace.id
        List<User> userList = sqlSession.selectList("userMapper.findAll");
        sqlSession.close();
        return userList;
    }
}

代理开发方式

需要遵循以下规范:

  1. Mapper.xml 文件中的 namespace 与 mapper 接口的全限定类名相同。
  2. mapper 接口的方法名和 Mapper.xml 中定义的每个 statement 的 id 相同。
  3. mapper 接口的输入参数类型和 Mapper.xml 中定义的每个 sql 的 paramerType 的类型相同。
  4. mapper 接口的返回参数类型和 Mapper.xml 中定义的每个 sql 的 resultType 的类型相同。
<mapper namespace="com.demo.dao.UserMapper">mapper>
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.findAll();

配置文件

SqlMapConfig.xml

  1. properties

<properties resource="jdbc.properties"/>


<property name="driver" value="${jdbc.driver}"/>
  1. settings:调整 mybatis 的运行时行为
<settings>
    
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    
    <setting name="cacheEnabled" value="true"/>
settings>
  1. typeAliases:定义别名
<typeAliases>
    <typeAlias type="com.demo.pojo.User" alias="user"/>
typeAliases>
  1. plugins:定义插件
<plugins>
    
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <property name="dialect" value="mysql"/>
    plugin>
plugins>
  1. environments:数据库环境
  2. mappers:定义 mapper.xml 的读取方式
<mappers>
    
    <mapper resource="UserMapper.xml"/>
    
    <package name="com.demo"/>
mappers>

Mapper.xml

动态 SQL

<select id="findByCondition" parameterType="user" resultType="user">
    select * from user
    <where>
        <if test="id!=0">
            and id = #{id}
        if>
        <if test="username!=null">
            and username = #{username}
        if>
    where>
select>

<select id="findByIds" parameterType="list" resultType="user">
    select * from user
    <where>
        
        <foreach collection="list" open="id in (" close=")" item="id" separator=",">
            #{id}
        foreach>
    where>
select>
List<Integer> list = Arrays.asList(1, 2, 3);
// SQL :  select * from user WHERE id in ( ? , ? , ? )
List<User> userList = userMapper.findByIds(list);
sql 片段抽取
<sql id="selectUser">select * from usersql>

<select id="findAll" resultType="user">
    <include refid="selectUser">include>
select>

复杂映射

假定一个数据模型,订单——用户关系。

一对一

一个订单只能属于一个用户。站在订单的角度,所以接口需要在订单类中编写。

@Data
@AllArgsConstructor
@NoArgsConstructor
// 订单信息
public class Order {
    private Integer id;
    private String orderTime;
    private Integer total;

    // 一个订单只能属于一个用户 在订单dao中定义的
    private User user;
}
<resultMap id="orderMap" type="com.demo.pojo.Order">
    <result column="uid" property="user.id"/>
    <result column="password" property="user.password"/>
    <result column="username" property="user.username"/>
resultMap>

<select id="findAll" resultMap="orderMap">
    select * from orders o,user u where o.uid = u.id
select>

也可以写成:

<resultMap id="orderMap" type="com.demo.pojo.Order">
    <result property="id" column="id"/>
    <result property="orderTime" column="order_time"/>
    <result property="total" column="total"/>
    
    <association property="user" javaType="user">
        <result column="uid" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
    association>
resultMap>
一对多

一个用户可以有多个订单。站在用户的角度,所以接口需要在用户类中编写。

private List<Order> orderList;
<resultMap id="userMap" type="com.demo.pojo.User">
    <result column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="password" property="password"/>
    
    <collection property="orderList" ofType="com.demo.pojo.Order">
        <result column="oid" property="id"/>
        <result column="total" property="total"/>
        <result column="order_time" property="orderTime"/>
    collection>
resultMap>

<select id="findWithOrders" resultMap="userMap">
    select *,o.id oid from user u left join orders o on u.id=o.uid
select>
多对多

多对多 和 一对多 的原理一样。只是查询的时候需要定义一张中间表。

假定一个数据模型,角色——用户关系。一个用户可以有多个角色,一个角色可以属于多个用户。

此处我们站在用户的角度查询,这个用户有什么角色。

<resultMap id="userRoleMap" type="user">
    <result column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="password" property="password"/>
    
    <collection property="roleList" ofType="com.demo.pojo.Role">
        <result column="rid" property="id"/>
        <result column="role_name" property="roleName"/>
    collection>
resultMap>

<select id="findWithRoles" resultMap="userRoleMap">
    select u.*,r.*,r.id rid from user u left join user_role ur on u.id=ur.user_id inner join role r on ur.role_id=r.id
select>

注解开发

简单注解:

@Insert("insert into user (id,username) values (#{id},#{username})")
int saveUser(User user);

@Update("update user set username = #{username} where id = #{id}")
void updateUser(User user);

@Delete("delete from user where id = #{id}")
void deleteUser(Integer id);

@Select("select * from user where id = #{id}")
User findUserById(Integer id);

// 根据 用户 id 查询角色
@Select("select * from role r,user_role ur where r.id=ur.role_id and ur.user_id = #{uid}")
List<Role> findByUid(Integer uid);

// 根据 用户 id 查询订单
@Select("select * from orders where uid = #{uid}")
List<Order> findByUid(Integer uid) throws Exception;

一对一

// 当前订单属于哪个用户 一对一
@Select("select * from orders")
@Results({
    @Result(id = true, property = "id", column = "id"),
    @Result(property = "orderTime", column = "order_time"),
    @Result(property = "total", column = "total"),
    @Result(property = "user", column = "uid", javaType = User.class,
            one = @One(select = "com.demo.dao.UserMapper.findUserById"))
})
List<Order> findAll();

一对多

// 查询每个用户的订单 一个用户有多个订单 一对多
@Select("select * from user")
@Results({
    @Result(id = true, property = "id", column = "id"),
    @Result(property = "username", column = "username"),
    @Result(property = "password", column = "password"),
    @Result(property = "orderList", column = "id", javaType = List.class,
            many = @Many(select = "com.demo.dao.OrderMapper.findByUid"))
})
List<User> findWithOrders();

多对多

// 查询每个用户的角色 一个用户可以有多个角色
@Select("select * from user")
@Results({
    @Result(id = true, property = "id", column = "id"),
    @Result(property = "username", column = "username"),
    @Result(property = "password", column = "password"),
    @Result(property = "roleList", column = "id", javaType = List.class,
            many = @Many(select = "com.demo.dao.RoleMapper.findByUid"))
})
List<User> findWithRoles()

缓存

一级缓存

默认开启的。sqlSession 级别。

SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 只打印一次 SQL
User user1 = userMapper.findUserById(1);
System.out.println(user1);

User user2 = userMapper.findUserById(1);
System.out.println(user2);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

// 中间穿插 更新操作 sqlSession.commit() 会清空缓存
User user1 = userMapper.findUserById(1);
System.out.println(user1);

User user = new User();
user.setId(3);
user.setUsername("zz");
userMapper.saveUser(user);

User user2 = userMapper.findUserById(1);
System.out.println(user2);

二级缓存

默认关闭。namespace 级别。

<settings>
    
    <setting name="cacheEnabled" value="true"/>
settings>

<cache/>
// 创建 两个 sql session
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();

UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

User user1 = userMapper1.findUserById(1);
System.out.println(user1);

sqlSession1.close(); //第一次查询完后关闭sqlSession 只打印一次 SQL

User user2 = userMapper2.findUserById(1);
System.out.println(user2);

注解模式下开启二级缓存:

@CacheNamespace
public interface UserMapper{}

二级缓存整合reids。


<dependency>
    <groupId>org.mybatis.cachesgroupId>
    <artifactId>mybatis-redisartifactId>
    <version>1.0.0-beta2version>
dependency>
# redis.properties
host=192.168.84.145
port=6379
connectionTimeout=5000
password=
database=1
<cache type="org.mybatis.caches.redis.RedisCache"/>
@CacheNamespace(implementation = RedisCache.class)

插件

自定义插件

@Intercepts({
    // 可以拦截的方法 Executor、StatementHandler、ParameterHandler、ResultSetHandler
    @Signature(type = StatementHandler.class,
               method = "prepare",
               args = {Connection.class, Integer.class})
})
public class CustomPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("自定义插件...");

        // 继续执行
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        System.out.println("将要包装的⽬标对象:" + target);
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的初始化参数:" + properties);
    }
}
<plugins>
    <plugin interceptor="com.demo.plugins.CustomPlugin">
        <property name="name" value="Hello Plugin"/>
    plugin>
plugins>

分页插件


<dependency>
    <groupId>com.github.pagehelpergroupId>
    <artifactId>pagehelperartifactId>
    <version>3.7.5version>
dependency>
<dependency>
    <groupId>com.github.jsqlparsergroupId>
    <artifactId>jsqlparserartifactId>
    <version>0.9.1version>
dependency>
<plugin interceptor="com.github.pagehelper.PageHelper">
    <property name="dialect" value="mysql"/>
plugin>
@Test
public void testPageHelper() {
    // 分页插件 会自动拼接上 limit
    PageHelper.startPage(1, 1);
    UserMapper userMapper = openSession().getMapper(UserMapper.class);
    List<User> userList = userMapper.findAll();
    System.out.println(userList);

    PageInfo<User> pageInfo = new PageInfo<>(userList);
    System.out.println(pageInfo);
}

通用 mapper


<dependency>
    <groupId>tk.mybatisgroupId>
    <artifactId>mapperartifactId>
    <version>3.1.2version>
dependency>
<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
    <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
plugin>
public interface UserMapper extends Mapper<User> {}
// 此处不用写 mapper接口能直接调用 select 方法
User user = new User();
user.setId(1);
List<User> select = userMapper.select(user);

// 根据条件查询
Example example = new Example(User.class);
example.createCriteria().andEqualTo("id", 1);
List<User> userList = userMapper.selectByExample(example);

Mybatis-plus

CRUD 基本应用

<dependency>
    <groupId>com.baomidougroupId>
    <artifactId>mybatis-plus-boot-starterartifactId>
    <version>3.1.1version>
dependency>
<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>5.1.47version>
dependency>
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/bank?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
    username: root
    password: 123456
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 驼峰命名自动转换
    map-underscore-to-camel-case: true
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    // 主键自增策略
    @TableId(type = IdType.AUTO)
    private Integer id;
    // 解决字段不一致问题
    @TableField("username")
    private String username;
    // 该字段不加入查询 返回结果为 null
    @TableField(select = false)
    private String password;
    @TableLogic
    private Integer deleteFlag;
}
public interface UserMapper extends BaseMapper<User> {}

@Autowired
private UserMapper userMapper;
// springboot 启动类添加该注解 进行 mapper 接口扫描
@MapperScan("com.demo.mapper")

添加操作

@Test
void testInsert() {
    User user = new User();
    user.setUsername("zz");
    user.setPassword("123456");
    int insert = userMapper.insert(user);
    // 返回受影响行数
    System.out.println(insert);
}

删除操作

@Test
void testDeleteById() {
    userMapper.deleteById(4);
}

@Test
void testDeleteByMap() {
    Map<String, Object> columnMap = new HashMap<>();
    columnMap.put("username", "zhangsan");
    userMapper.deleteByMap(columnMap);
}

@Test
void testDeleteByWrapper() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("id", 4);
    userMapper.delete(wrapper);
}

@Test
void testDeleteByBatchIds() {
    userMapper.deleteBatchIds(Arrays.asList(2, 3, 4));
}

修改操作

@Test
void testUpdateById() {
    User user = new User();
    user.setId(4);
    user.setUsername("yy");
    user.setPassword("123456");
    userMapper.updateById(user);
}

@Test
void testUpdateByWrapper() {
    User user = new User();
    // 更新的字段
    user.setPassword("666666");
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // 条件字段
    wrapper.eq("id", 4);
    userMapper.update(user, wrapper);
}

查询操作

@Test
void testSelectById() {
    // 根据ID查询
    User user = userMapper.selectById(1);
    System.out.println(user);
}

@Test
void testSelectByBatchIds() {
    // select in
    List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 3, 4));
    System.out.println(userList);
}

@Test
void testSelectOne() {
    // 根据条件查询
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("id", 1);
    User user = userMapper.selectOne(wrapper);
    System.out.println(user);
}

@Test
void testSelectCount() {
    // 统计条目数
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("id", 1);
    Integer count = userMapper.selectCount(wrapper);
    System.out.println(count);
}
分页查询
@Configuration
public class MybatisConfig {
    // 分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}
@Test
void testSelectPage() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("id", 1);
    Page<User> page = new Page<>(1, 1);
    IPage<User> userIPage = userMapper.selectPage(page, wrapper);
    List<User> records = userIPage.getRecords();
    System.out.println(records);
}

条件构造器

@Test
void testSelectList() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();

    // 基本比较符
    wrapper.eq("id", 1); // id = 1
    wrapper.ne("id", 1); // id != 1
    wrapper.gt("id", 1); // id > 1
    wrapper.ge("id", 1); // id >= 1
    wrapper.lt("id", 1); // id < 1
    wrapper.le("id", 1); // id <= 1
    wrapper.between("id", 1, 4); // 1 <= id <=4
    wrapper.notBetween("id", 1, 4); // id < 1 && id > 4
    wrapper.in("id", "1", "2", "3");
    wrapper.notIn("id", "2", "3");

    // 模糊查询
    wrapper.like("username", "z"); // %z%
    wrapper.notLike("username", "z"); // not like %z%
    wrapper.likeLeft("username", "z"); // %z
    wrapper.likeRight("username", "z"); // z%

    // 排序
    wrapper.orderByAsc("id", "username");
    wrapper.orderByDesc("id", "username");

    // or & and
    wrapper.eq("id", 1).or().eq("id", 2);

    // 默认查询所有字段 指定查询字段
    wrapper.select("id", "username");

    List<User> userList = userMapper.selectList(wrapper);
    System.out.println(userList);
}

扩展自定义方法

public class FindAll extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sqlMethod = "findAll";
        String sql = "select * from " + tableInfo.getTableName();
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return addSelectMappedStatement(mapperClass, sqlMethod, sqlSource, modelClass, tableInfo);
    }
}
public class CustomSqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList() {
        List<AbstractMethod> methodList = super.getMethodList();
        methodList.add(new FindAll());
        return methodList;
    }
}
// springboot 配置类添加自定义配置
@Bean
public CustomSqlInjector customSqlInjector() {
    return new CustomSqlInjector();
}
// 将自定义的 mapper 类继承 BaseMapper 进行扩展
public interface CustomBaseMapper<T> extends BaseMapper<T> {
    List<T> findAll();
}

public interface UserMapper extends CustomBaseMapper<User> {}

@Test
void testCustomFindAll(){
    List<User> all = userMapper.findAll();
    System.out.println(all);
}

逻辑删除

// 在实体类中定义逻辑删除字段
@TableLogic
private Integer deleteFlag;
mybatis-plus:
  global-config:
    db-config:
      # 逻辑已删除值(默认为 1)
      logic-delete-value: 1
      # 逻辑未删除值(默认为 0)
      logic-not-delete-value: 0

参考git:https://gitee.com/zhangyizhou/learning-mybatis-demo.git

你可能感兴趣的:(java,mysql,后端)