mybatis配置数据映射的方式有哪些?

  1. xml 方式
  2. 注解方式

    mybatis注解开发【掌握】

    1、概述

什么是MyBatis的注解方式?
MyBatis的注解方式就是将SQL语句直接写在接口上。在MyBatis注解SQL中,最基本的就是@Select、@Insert、@Update和@Delete四种。

MyBatis注解方式的优缺点

  • 优点:对于需求比较简单的系统,开发效率高。
  • 缺点:当SQL有变化时都需要重新编译代码,一般情况下不建议使用注解方式

2、注解实现CRUD

说明:在演示注解开发之前,为了避免和之前的映射文件混淆,所以可以将之前书写的代码放到一个新的工程中,删除映射文件即可。

一个功能不能同时用xml和注解方式,要二选一。不同的功能,可以自由选择xml或者注解

2.0、CRUD相关注解

【注解】

  1. @Insert:保存
  2. Valuesql语句(和xml的配置方式一模一样)
  3. @Update:更新
  4. Valuesql语句
  5. @Delete: 删除
  6. Valuesql语句
  7. @Select: 查询
  8. Valuesql语句
  9. @Options:可选配置(获取主键)
  10. userGeneratedKeys:开关,值为true表示可以获取主键 相当于select last_insert_id()
  11. keyProperty :对象属性
  12. keyColumn : 列名

【使用方式】
【第一步】将mybatis全局配置文件mybatis-config.xml中的mapper路径改为包扫描或者指定class路径;

  1. <configuration>
  2. ...
  3. <mappers>
  4. <!--接口所在包-->
  5. <package name="com.itheima.dao"/>
  6. </mappers>
  7. </configuration>

【第二步】编写接口和注解;
【第三步】测试

2.1、新增

目标:使用注解@Insert的方式新增数据
步骤:

第一步:UserMapper接口中新增用户方法上面编写注解; 第二步:测试

实现:
第一步:在UserMapper接口中的saveUser()方法上面添加@Insert注解,并设置该注解的value属性值为具体的SQL语句;

  1. public interface UserMapper {
  2. //1、新增数据 #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
  3. @Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
  4. void saveUser(User user);
  5. }

第二步:测试
在UserMapperTest类下面对save方法进行测试:

  1. import com.itheima.dao.UserMapper;
  2. import com.itheima.pojo.User;
  3. import org.apache.ibatis.io.Resources;
  4. import org.apache.ibatis.session.SqlSession;
  5. import org.apache.ibatis.session.SqlSessionFactory;
  6. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  7. import org.junit.AfterClass;
  8. import org.junit.BeforeClass;
  9. import org.junit.Test;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. public class MyBatisTest01 {
  13. public static UserMapper mapper;
  14. public static SqlSession session;
  15. @BeforeClass
  16. public static void init() throws IOException {
  17. // 所有测试方法执行前初始化
  18. String resource = "mybatis-config.xml";
  19. InputStream inputStream = Resources.getResourceAsStream(resource);
  20. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  21. session = sqlSessionFactory.openSession(true); //设置事务为自动提交
  22. mapper = session.getMapper(UserMapper.class);
  23. }
  24. @AfterClass
  25. public static void release() {
  26. //释放会话资源
  27. session.close();
  28. }
  29. @Test
  30. public void saveUser(){
  31. User user = new User();
  32. user.setUserName("大圣");
  33. user.setAge(500);
  34. user.setName("孙悟空");
  35. user.setPassword("1234");
  36. user.setSex(1);
  37. userMapper.saveUser(user);
  38. }
  39. }

小结:
新增数据的注解为:@Insert ,作用等同于xml映射文件中的<insert>具体使用时,需要给其value属性设置具体的SQL。

新增数据时id回填(自增主键回填)了解

目标:使用注解完成数据新增,新增成功后返回数据的主键id值
步骤:

1、在新增数据注解 @Insert下面,添加@Options; 2、在Options注解中,设置useGeneratedKeys值为true,keyProperty为id,keyColumn id;

实现:
第一步:在新增数据注解 @Insert下面,添加@Options,设置useGeneratedKeys值为true,keyProperty为id,keyColumn 为id;

  1. //1、新增数据 #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
  2. @Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
  3. @Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id")
  4. int saveUser(User user);

第二步:测试:

  1. @Test
  2. public void saveUserTest() {
  3. User user = new User();
  4. user.setId(null); //主键自增
  5. user.setUserName("大圣");
  6. user.setPassword("12345");
  7. user.setName("孙悟空");
  8. user.setSex(2);
  9. mapper.saveUser(user);
  10. System.out.println("user = " + user);
  11. }

User{id=15, userName=’大圣’, password=’12345’, name=’孙悟空’, age=null, sex=2}

小结:注解@Options
主键回填到插入对象中:

@Options(useGeneratedKeys = true,keyColumn = “id”,keyProperty = “id”)

  1. keyColumn 配置表中主键列的名字
  2. keyProperty 配置pojo中主键字段的属性名


2.2、删除

目标:使用注解@Delete删除id值为1的数据
步骤:

第一步:在根据id删除数据的方法上面编写注解@Delete; 第二步:测试

实现:
第一步:在UserMapper接口中的deleteUserById方法上编写@Delete,并设置其value属性值为具体的删除SQL;

  1. /*
  2. 2.根据id删除用户
  3. */
  4. @Delete("delete from tb_user where id=#{id}")
  5. int deleteUserById(Long id);

第二步:测试

  1. @Test
  2. public void deleteUserById(){
  3. userMapper.deleteUserById(1L);
  4. }

小结:
删除数据的注解:@Delete,作用等同于映射文件中的<delete>,具体使用时,需要设置其value属性值为具体的删除SQL;

2.3、修改

目标:修改id为1的用户的数据
步骤:

第一步:在根据id修改用户数据方法上面添加注解@Update,然后在其value属性值中编写具体的SQL; 第二步:测试

实现:
第一步:在UserMapper接口的updateUser方法上添加注解:@Update,然后将其value属性值设置成update的SQL;

  1. /**
  2. * 3.修改用户数据
  3. * @param user
  4. */
  5. @Update("UPDATE tb_user SET user_name=#{userName}, password=#{password} ,name=#{name} ,age=#{age},sex=#{sex} where id=#{id}")
  6. int updateUser(User user);

第二步:测试

  1. @Test
  2. public void updateUser(){
  3. User user = new User();
  4. user.setId(1L);
  5. user.setUserName("柳岩");
  6. user.setSex(0);
  7. user.setPassword("3456");
  8. user.setName("岩岩");
  9. user.setAge(20);
  10. userMapper.updateUser(user);
  11. }

小结:修改数据的注解:@Update,作用等同于映射文件中的<update>

2.4、查询

目标:使用注解查询所有的用户数据
步骤:

第一步:在接口中查询所有的用户数据的方法上面添加注解:@Select,然后设置其value属性值为具体的SQL查询语句; 第二步:测试

实现:
第一步:在UserMapper接口的queryAllUsers方法上添加注解:@Select,然后设置其value属性值为具体的查询SQL;

  1. /*
  2. * 4.查询所有用户数据
  3. */
  4. @Select("SELECT * FROM tb_user")
  5. List<User> queryAllUsers();

第二步:测试

  1. @Test
  2. public void queryAllUsers(){
  3. List<User> list = userMapper.queryAllUsers();
  4. System.out.println("list = " + list);
  5. }

小结:
查询数据注解:@Select ,作用等同于映射文件中的<select>标签。

3、注解实现别名映射

根据之前的学习,如果数据表的列名和pojo实体类的属性名不一致,会导致数据表的数据无法封装到实体类属性值中,对此我们又如下解决方案:

【1】查询的时候给列起别名,别名和实体类的属性名一致

  1. select user_name as userName from tb_user where id =1;

【2】在mybatis的核心配置文件中按照如下配置:

<settings>
    <!--开启驼峰自动映射-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

【3】在映射文件中,我们可以通过在ResultMap中,通过result标签中,给指定的column映射到实体类中指定的属性上。

<resultMap id="orderAndUserResultRelative" type="Order">
     <result column="user_name" property="userName"/>
 </resultMap>

而在注解中也有相应的解决方案:这里就必须使用注解:@Results
@Results注解相当于之前映射文件中的ResultMap,该注解如下:

public @interface Results {
    Result[] value() default {};
}

我们发现value属于Result数组类型,而Result属于一个注解,注解的属性如下:

public @interface Result {
    //对应数据表的列
    String column() default "";
    //对应pojo类的属性
    String property() default "";
    //javaType:返回的对象类型
    Class<?> javaType() default void.class;
    //one: 一对一配置
    One one() default @One;
    //many: 一对多配置
    Many many() default @Many;
}

需求:查询指定id的用户。使用注解映射用户名字段。
为了演示pojo属性名和表中字段名不一样,导致自动映射失败的情况。我们修改表中字段名,让表中用户名字段名和pojo中用户名的属性名不一样,且没有驼峰和经典下划线映射关系。
4 mybatis-3 - 图1
实现:
第一步:在接口中查询方法上面添加注解:@Results ,然后通过@Result完成字段的别名和实体类的属性名之间的映射配置;
说明:这里我们使用之前的查询所用用户方法即可,也可以在接口中在定义一个根据id查询用户的方法。

/*
        根据id查询用户
 */
    @Results({
            @Result(column = "user_name11", property = "userName"),
    })
    @Select("select * from tb_user where id=#{id}")
    User selectById(Long id);

第二步:测试

@Test
public void selectById(){
    User user = userMapper.selectById(1L);
    System.out.println("user = " + user);
}

【结果】
user = User{id=1, userName=’zhangsan’, password=’123456’, name=’张三’, age=30, sex=1}

小结:给别名映射到实体类中可以通过添加注解:@Results 中的 @Result实现;

4 动态sql(理解)

【需求】:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户
正常的sql语句:查询男性并且用户名中包含zhang
4 mybatis-3 - 图2

select * from tb_user where sex = "男" and user_name like '%zhang%'

4 mybatis-3 - 图3

select * from tb_user where  sex = "男"
实现需求时还要判断用户是否输入用户名来做不同的查询要求,而这里似乎没有办法判断是否输入了用户名,因此可以考虑使用动态sql来完成这个功能。<br />    上述动态sql语句部分: and user_name like '%zhang%'

注解开发时,Mybatis给我们提供了三种编写动态sql的方式:

方式1: 脚本标签 (自己课下学习,课上不讲解)

在接口上添加: <script></script>标签,标签体中编写sql语句,sql语句与之前配置文件中的一致

//【需求】:查询**男性**用户,**如果输入了用户名,按用户名模糊查询**,如果**没有输入用户名,就查询所有男性用户**。
@Select("<script>select * from tb_user where sex=#{sex} <if test=\"username!=null and username.trim()!=''\">and user_name like concat('%',#{username},'%')</if></script>")
List<User> queryUsersBySexOrUsername(@Param("sex") String sex,@Param("username") String username);

说明:这种方式在写法上面和 XML 中的写法是一样,支持 XML 的动态SQL语法,可以在上面的字符串中写 **<foreach>** 标签的语法。
测试类:

@Test
public void queryUsersBySexOrUsername() {
    List<User> list = mapper.queryUsersBySexOrUsername("男", "lisi");
    System.out.println("list = " + list);
}

方式2:_@_SelectProvider

使用 _@_SelectProvider 注解,注解中的type 参数是提供构建 SQL 的类,method 是构建 SQL 的方法。构建 SQL 的方法的参数要和接口的参数一致,并且多个参数要使用命名参数。
接口:

@SelectProvider(type= ProviderUtils.class,method = "queryUsersBySexOrUsernameSQL")
 List<User> queryUsersBySexOrUsername(@Param("sex") String sex,@Param("username") String username);

动态sql生成类

import org.apache.ibatis.annotations.Param;
public class ProviderUtils {
    public String queryUsersBySexOrUsernameSQL(@Param("sex") String sex, @Param("username") String username){
        String sql = "select * from tb_user  where sex = #{sex}";
        if (username!=null && !"".equals(username)){
            sql += " and user_name like concat('%',#{username},'%')";
        }
        return  sql;
    }
}

测试类:

@Test
public void queryUsersBySexOrUsername() {
    List<User> list = mapper.queryUsersBySexOrUsername("男", "lisi");
    System.out.println("list = " + list);
}

方式3: _@_SelectProvider

上一中方式时直接拼接字符串,容易出错。比如拼接的过程中少个空格就会出问题。在生成SQL时,也可以使用 Mybatis 提供的org.apache.ibatis.jdbc.SQL类,可以很方便我们生成SQL,如下:

public class ProviderUtils {
    public String queryUsersBySexOrUsernameSQL2(@Param("sex") String sex, @Param("username") String username) {
        //借助mybatis提供的一个对象:SQL
        //创建SQL对象
       SQL sql = new SQL();
       //链式编程,每个方法返回值都是SQL类对象
       sql.SELECT("*").FROM("tb_user").WHERE("sex = #{sex}");
       //判断用户是否为空,不为空就继续链式编程,即继续拼接
       if(username != null && !"".equals(username)){
           sql.WHERE("user_name like concat('%',#{username},'%')");
       }
       //SELECT * FROM user WHERE (sex=? AND user_name like concat('%',?,'%')) 
       //转换为字符串并返回
       return sql.toString();   
    }
}

接口方法使用注解 _@_SelectProvider 引用工具类中的方法

@SelectProvider(type= ProviderUtils.class,method = "queryUsersBySexOrUsernameSQL2")
 List<User> queryUsersBySexOrUsername(@Param("sex") String sex,@Param("username") String username);

测试类:

@Test
    public void queryUsersBySexOrUsername() {
        List<User> list = mapper.queryUsersBySexOrUsername("男", "lisi");
        System.out.println("list = " + list);
    }