Mybatis注解开发

Mybatis中有两种开发方式

  • xml开发,同映射配置文件
  • 注解开发
    这里要讲的是注解开发

为什么会有注解开发?

因为xml开发的配置,过于繁琐,为了提升开发效率,有了注解开发,只要单表查询的都可以使用注解开发,但如果结果集的数据来自于多张表查出来的,那还是更建议使用一对一、一对多、多对多的方式,xml和注解是可以共存的。

流程

1.不需要再使用 xxxxMapper.xml 映射配置文件,取之替代的,需要在 mybatis-config.xml 核 心配置文件中,将映射器的属性改为<package name="扫描的包路径">来让他自动扫描接口
2.编写接口和注解
3.测试

为什么不使用注解开发进行多表查询?

因为使用注解开发的时候,会先进行单表查询,然后再基于前一次单表查询的数据再继续查询另一张表,会存在多次查询的一个情况,会导致性能慢

前置准备

SQL表

/*
Navicat MySQL Data Transfer

Source Server         : localhost
Source Server Version : 50622
Source Host           : localhost:3306
Source Database       : heima81

Target Server Type    : MYSQL
Target Server Version : 50622
File Encoding         : 65001

Date: 2019-08-20 17:08:12
*/

SET FOREIGN_KEY_CHECKS=0;


-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(100) DEFAULT NULL COMMENT '用户名',
  `password` varchar(100) DEFAULT NULL COMMENT '密码',
  `name` varchar(100) DEFAULT NULL COMMENT '姓名',
  `age` int(10) DEFAULT NULL COMMENT '年龄',
  `sex` int(11) DEFAULT NULL COMMENT '0-女 1-男',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES ('1', 'zhangsan', '123456', '张三', '30', '1');
INSERT INTO `tb_user` VALUES ('2', 'lisi', '123456', '李四', '21', '0');
INSERT INTO `tb_user` VALUES ('3', 'wangwu', '123456', '王五', '22', '1');
INSERT INTO `tb_user` VALUES ('4', 'zhangwei', '123456', '张伟', '20', '1');
INSERT INTO `tb_user` VALUES ('5', 'lina', '123456', '李娜', '28', '0');
INSERT INTO `tb_user` VALUES ('6', '蔡徐坤', '123', '小菜', '18', '1');

对应映射类


// user
@Data  
@AllArgsConstructor  
@NoArgsConstructor  
public class User implements Serializable{  
    private Long id;  
    // 用户名  
    private String userName;  
    // 密码  
    private String password;  
    // 姓名  
    private String name;  
    // 年龄  
    private Integer age;  
    //0-女 1-男  
    private Integer sex;  
  
}

Mybatis工具类

package com.dongmianmao.util;  
  
import org.apache.ibatis.session.SqlSession;  
import org.apache.ibatis.session.SqlSessionFactory;  
import org.apache.ibatis.session.SqlSessionFactoryBuilder;  
  
import java.io.InputStream;  
  
public class MybatisUtil {  
    static SqlSessionFactory ssf = null;  
  
    private MybatisUtil(){}  
  
    static{  
        String resource = "mybatis-config.xml";  
        InputStream resourceAsStream = MybatisUtil.class.getClassLoader().getResourceAsStream(resource);  
        ssf = new SqlSessionFactoryBuilder().build(resourceAsStream);  
    }  
  
    public static SqlSession OpenSession(){  
        return ssf.openSession();  
    }  
  
    public static SqlSession OpenSession(Boolean bool){  
        return ssf.openSession(bool);  
    }  
}

一、Insert 体验

需求:往 User 表中插入一条记录
在以往使用xml开发,需要编写以下映射配置文件

<insert id="addUser" >  
    insert into tb_user(user_name, password, name, age, sex) values('zhangsan1',123,'张三',12,1)  
</insert>

当需求多了后会相对繁琐,而注解开发则是在一般的单表查询不需要使用编写xml,而是像一下这样,在对应的映射接口中使用注解

@Insert("insert into tb_user(user_name, password, name, age, sex) " +  
            "values(#{userName},#{password},#{name},#{age},#{sex})")  
int addUser(User user);  

使用 @Insert() 注解,在里面同 xml 编写SQL一致,在括号内编写 SQL语句 , 使用 #{} 来进行占位
测试类

@Test  
public void testAddUser(){  
    SqlSession sqlSession = MybatisUtil.OpenSession();  
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
    User user = new User();  
    user.setUserName("zhangsan11");  
    user.setPassword("123456");  
    user.setName("张三11");  
    user.setAge(12);  
    user.setSex(1);  
    int i = mapper.addUser(user);  
    if (i>0){  
        sqlSession.commit();  
        System.out.println("添加成功");  
    }  
    sqlSession.close();  
}

运行以上测试代码,就可以看到控制台输出,添加成功

二、Select 体验

需求:查询所有用户
在以往使用xml开发,需要编写以下映射配置文件

<select id="findAllUser" resultType="user">  
    select id, user_name, password, name, age, sex from tb_user;  
</select>

而使用注解开发,只需要使用 @Select() 并在里面写对应查询语句即可

@Select("select id, user_name, password, name, age, sex from tb_user")  
List<User> findAllUser();

测试类

@Test  
public void testFindAllUser(){  
    SqlSession sqlSession = MybatisUtil.OpenSession();  
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
    List<User> allUser = mapper.findAllUser();  
    for (User user : allUser) {  
        System.out.println(user);  
    }  
    sqlSession.close();  
}

就可以查出对应的记录了

三、Update 体验

需求:修改指定用户名的用户密码
注解开发,只需要使用 @Update() 并在对应接口里面写对应查询语句即可

@Update("update tb_user set password=#{password} where user_name=#{username}")  
int updateUserPassword(@Param("username")String username,  
                       @Param("password")String password);

因为传入了一个以上的参数,为了让Mybatis区分两个参数,需要使用 @Param() 来给后面的参数取名字
测试类

@Test  
public void testUpdateUserPassword(){  
    SqlSession sqlSession = MybatisUtil.OpenSession();  
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
    int i = mapper.updateUserPassword("zhangsan11", "111111");  
    if (i>0){  
        sqlSession.commit();  
        System.out.println("修改密码成功");  
    }  
    sqlSession.close();  
}

这样就可以修改指定的用户名密码了

四、Delete 体验

需求:根据传入id删除指定记录
注解开发,只需要使用 @Delete() 并在对应接口里面写对应查询语句即可

@Delete("delete from tb_user  where id = #{id}")  
int deleteUser(int id);

可以看到,在 deleteUser 这个方法中传入了一个 id 参数,用于删除指定用户
测试类

@Test  
public void testDeleteUser(){  
    SqlSession sqlSession = MybatisUtil.OpenSession();  
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
    int i = mapper.deleteUser(15);  
    if (i>0){  
        sqlSession.commit();  
        System.out.println("删除用户成功");  
    }  
    sqlSession.close();  
}

这样就可以删除指定 id 的行记录了

五、Results结果集映射

当查询结果列名和实体类属性名不一致时有以下解决方案

  • 1.在SQL中,添加别名—as(别名和实体类中的属性名一致)
  • 2.在核心配置文件中设置驼峰映射
  • 3.使用resultMap标签,设置查询结果和实体类的映射关系
    这里讲解决方案3,在注解中如何使用
    在注解开发中,有一个全新的注解 @Resluts 实现映射,等价于 resultMap 标签

可以先看一下在 xml 开发中,配置结果集映射是怎么样的

<resultMap id="userMap" type="com.dongmianmao.Pojo.User" autoMapping="true">  
    <id column="user_id" property="id"/>  
    <result column="user_name" property="userName"/>  
</resultMap>

<id><result>标签分别表示 id 和除了 id 以外的字段

看一下==@Results== 的源码

@Documented  
@Retention(RetentionPolicy.RUNTIME)  
@Target({ElementType.METHOD})  
public @interface Results {  
    String id() default "";  
  
    Result[] value() default {};  
}

可以发现,前面有一个 id 属性 , 后面跟着一个 Result 数组 叫 value
在进入 Result 源码中
![[Pasted image 20250316165104.png]]

只有前面三个是需要使用的,而后面的,都是关于多表查询的,由于前面说了性能慢,这里只提单表查询需要使用的
而在注解开发中区分主键,是不像 xml 开发中一样有个专门的标签的,在注解开发中,所有字段都是围绕着末尾这两个属性展开,而区分id的方法,则是在数组元素中的 @Result 中,添加一个 id=true 属性

需求:将查询全部User方法中的SQL语句中的字段与实体类的映射字段一一对应
先针对先前的查询语句进行一个结果集映射

@Select("select id, user_name, password, name, age, sex from tb_user")  
@Results(id = "resultMap" ,  
            value = {  
                @Result(column = "id" , property = "id"  , id=true),  
                @Result(column = "user_name",property = "userName")  
            })  
List<User> findAllUser();

只对不一致的字段和id进行指定,其他的会自动添加
测试类同上,一开始的测试类如果没开驼峰映射,可能会导致userName是Null的情况

@Test  
public void testFindAllUser(){  
    SqlSession sqlSession = MybatisUtil.OpenSession();  
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
    List<User> allUser = mapper.findAllUser();  
    for (User user : allUser) {  
        System.out.println(user);  
    }  
    sqlSession.close();  
}

六、模糊查询

需求:查询男性用户,如果输入了用户名,按用户模糊查询,如果没有输入用户名,那就查询所有男性用户
根据需求写出的SQL

select id, user_name, password, name, age,sex  
from  tb_user 
where  sex = 1 and name like '%张%';

然后在接口中定义一个方法,来查询用户

	/**  
 * 根据传入的用户名来进行查询用户  
 * @param name  用户名
 * @return  用户列表
 */  
@Select("select id, user_name, password, name, age,sex\n" +  
        "from  tb_user where  sex = 1 and name like '%#{name}%';")  
List<User> findUserByLike(String name);

可以看到,最后的 ‘%#{name}%’ 这个占位符是在单引号里的,是不生效的
有两种做法

1.使用传参的方式把整个格式给包括了
	/**  
 * 根据传入的用户名来进行查询用户  
 * @param name  用户名
 * @return  用户列表
 */  
@Select("select id, user_name, password, name, age,sex\n" +  
        "from  tb_user where  sex = 1 and name like '#{name};")  
List<User> findUserByLike(String name);

修改为这样,然后测试类

@Test  
public void testFindUserByLike(){  
    SqlSession sqlSession = MybatisUtil.OpenSession();  
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
    List<User> list = mapper.findUserByLike("%张%");  
    for (User user : list) {  
        System.out.println(user);  
    }  
    sqlSession.close();  
}

直接把 % 传入进去,也能符合需求,但实际开发中,不会这样干,更多的还是使用第二种方式

2.使用==concat()==来拼接SQL语句

这里先修改接口方法中的SQL语句

	/**  
 * 根据传入的用户名来进行查询用户  
 * @param name  用户名
 * @return  用户列表
 */  
@Select("select id, user_name, password, name, age,sex\n" +  
        "from  tb_user where  sex = 1 and name like concat('%',#{name},'%');")  
List<User> findUserByLike(String name);

相应的,测试类修改为

@Test  
public void testFindUserByLike(){  
    SqlSession sqlSession = MybatisUtil.OpenSession();  
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
    List<User> list = mapper.findUserByLike("张");  
    for (User user : list) {  
        System.out.println(user);  
    }  
    sqlSession.close();  
}

最后也符合需求

七、动态SQL

有两种方式可以实现

1.使用 @xxxProvider() 注解(xxx:对应的增删改查操作)

需求:查询男性用户,如果输入了用户名,按用户模糊查询,如果没有输入用户名,那就查询所有男性用户
这里针对 六、模糊查询的需求进行优化,因为那的写法,无法实现后面部分的需求,如果没有输入用户名,那就查询所有男性用户

因为这里是关于 Select 的操作,所以使用,@SelectProvider(),使用这个注解需要先创建一个类,在一个类中来书写关于SQL的判断规则,这里我们创建一个叫做 SqlProvider 的类,

package com.dongmianmao.Dao;  
  
import org.apache.ibatis.annotations.Param;  
import org.junit.Test;  
  
public class SqlProvider {  
    public String findUserByLike(@Param("name")String name){  
        String sql = "select id, user_name as userName, password, name, age,sex from  tb_user where  sex = 1";  
        if (name!=null  && !name.equals("")){  
            sql+=" and name like concat('%',#{name},'%')";  
        }  
        return sql;  
    }  
}

可以看到,在这里面写了一个方法 findUserByLike 来对SQL进行判断,默认的SQL是查询所有男性记录
如果传入的的 name 不为空也不为 null 则在最开始查出所有男性记录的SQL的基础上,拼接模糊查询,最后返回SQL

创建好类后,接下来修改接口内的方法

    /**  
     * 根据传入的用户名来进行查询用户  
     * @param name  
     * @return  
     */  
//    @Select("select id, user_name as userName, password, name, age,sex" +  
//            "from  tb_user where  sex = 1 and name like concat('%',#{name},'%');")  
    @SelectProvider(type = SqlProvider.class,method = "findUserByLike")  
    List<User> findUserByLike(@Param("name") String name);

把最开始使用的 @Selecty 给注释了,不再使用这个,而是使用 @SelectProvider ,这里面有两个参数

参数名参数值参数作用是否必须
type写有动态sql规则的类的字节码文件找到书写动态sql规则的类
method在书写动态sql规则的类中的规则方法通过字节码文件拿到成员方法再去执行
在字节码文件中去执行成员方法,还需要去指定对应的入参,而这个入参则是在 接口 和 类中的入参 使用==@Parma== 去指定一样的名字
这里的 规则方法接口 中均指定了入参为 @Parma(“name”) ,==必须得一致 ==

测试类

@Test  
public void testFindUserByLike(){  
    SqlSession sqlSession = MybatisUtil.OpenSession();  
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
    List<User> list = mapper.findUserByLike("张");  
    for (User user : list) {  
        System.out.println(user);  
    }  
    sqlSession.close();  
}

运行后就实现了对应的需求

2.使用 @xxxProvider() 注解 + SQL类

本质上和第一种方法是一样的,只不过不一样的是,第一种方法使用的是字符串直接拼接,而第二种放啊使用的是Mybatis中提供的一个对象 SQL 中的 API方法 来进行 SQL 语句的拼接
即原来的 SqlProvider 类中的方法改为

package com.dongmianmao.Dao;  
  
import com.sun.org.apache.regexp.internal.RE;  
import org.apache.ibatis.annotations.Param;  
import org.apache.ibatis.jdbc.SQL;  
import org.junit.Test;  
  
public class SqlProvider {  
    public String findUserByLike(@Param("name")String name){  
        SQL sql = new SQL();  
        sql=sql.SELECT("*").FROM("tb_user").WHERE("sex = 1");  
        if (name!=null && !name.equals("")){  
            sql.AND().WHERE("name like concat('%',#{name},'%')");  
        }  
        return sql.toString();  
    }  
}

API 的取名如实际效果,替代了原有的关键字

接口方法一致不做任何改变

    /**  
     * 根据传入的用户名来进行查询用户  
     * @param name  
     * @return  
     */  
//    @Select("select id, user_name as userName, password, name, age,sex" +  
//            "from  tb_user where  sex = 1 and name like concat('%',#{name},'%');")  
    @SelectProvider(type = SqlProvider.class,method = "findUserByLike")  
    List<User> findUserByLike(@Param("name") String name);

测试类

@Test  
public void testFindUserByLike(){  
    SqlSession sqlSession = MybatisUtil.OpenSession();  
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
    List<User> list = mapper.findUserByLike("张");  
    for (User user : list) {  
        System.out.println(user);  
    }  
    sqlSession.close();  
}

运行后可以看到,符合预期

总结:相对第一种方法来讲,第二种方法会更麻烦一点,没有第一种方法来的快,还是看个人习惯

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值