Mybatis学习笔记 - 02

Mybatis系列文章

  • Mybatis学习笔记 - 01
  • Mybatis学习笔记 - 02
  • Mybatis学习笔记 - 03
  • Mybatis学习笔记 - 04

文章目录

    • 1. 添加操作
      • 1.1 在 UserMapper 接口中新增 `saveUser()` 方法
      • 1.2 在 Mapper 映射文件 `UserMapper.xml` 中配置添加操作
      • 1.3 测试添加操作
      • 1.4 测试结果
    • 2. 删除操作
      • 2.1 在 UserMapper 接口中新增 `removeUserById()` 方法
      • 2.2 在 Mapper 映射文件 `UserMapper.xml` 中配置删除操作
      • 2.3 测试删除操作
      • 2.4 测试结果
    • 3. 修改操作
      • 3.1 在 UserMapper 接口中新增 `updateUser()` 方法
      • 3.2 在 Mapper 映射文件 `UserMapper.xml` 中配置修改操作
      • 3.3 测试修改操作
      • 3.4 测试结果
    • 4. 查询操作
      • 4.1 根据id查询
      • 4.2 模糊查询
      • 4.3 使用聚合函数查询用户总数
    • 5. 扩展操作
      • 5.1 添加用户时获取 id 的返回值
      • 5.2 使用 pojo 包装类进行查询
      • 5.3 使用 resultMap 接收查询结果
    • 6. Mybatis 配置文件标签讲解
      • 6.1 `properties` 标签
      • 6.2 `typeAliases ` 标签


1. 添加操作


1.1 在 UserMapper 接口中新增 saveUser() 方法

public interface UserMapper {
    /**
     * 查询所有用户
     *
     * @return
     */
    List<User> listAllUsers();

    /**
     * 添加用户
     * @param user
     * @return 成功返回1,失败返回0
     */
    int saveUser(User user);
}

1.2 在 Mapper 映射文件 UserMapper.xml 中配置添加操作



<mapper namespace="cn.ykf.mapper.UserMapper">
    
    <select id="listAllUsers" resultType="cn.ykf.pojo.User">
        SELECT * FROM user
    select>
    
    <insert id="saveUser" parameterType="cn.ykf.pojo.User">
        INSERT INTO user(username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address})
    insert>
mapper>
  • 温馨提示
    • 在编写映射文件的时候,可以先在 MySQL 客户端写好可以正常运行的 SQL 语句,然后将其搬运到映射文件中,并把原本的实际参数都替换为 Mybatis 的参数符号
  • parameterType表示将会传入这条语句的参数类的完全限定名或别名。
  • 其中的参数符号#{username},#{birthday},#{birthday},#{sex},#{address}必须和User类的每个属性名一一对应,不可以乱写。
    • 属性名是实体类的getXxx()/setXxx()Xxx部份,大多数情况下就是成员变量名,也有少数情况不是成员变量名,也就是说成员变量和属性不能等同。
  • 关于配置文件的相关说明,可以查看官网文档:Mybatis的XML映射文件

1.3 测试添加操作

public class MybatisTest {

    private InputStream is;
    private SqlSession sqlSession;
    private UserMapper mapper;

    /**
     * 测试之前执行,用于初始化
     */
    @Before
    public void init() throws Exception {
        // 1. 读取配置文件
        is = Resources.getResourceAsStream("mybatis-config.xml");
        // 2. 创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        // 3. 获取SqlSession对象
        sqlSession = factory.openSession();
        // 4. 使用SqlSession创建Mapper的代理对象
        mapper = sqlSession.getMapper(UserMapper.class);
    }

    /**
     * 测试结束执行,用于提交事务和释放资源
     */
    @After
    public void destroy() throws Exception {
        // 6. 提交事务
        sqlSession.commit();
        // 7. 释放资源
        sqlSession.close();
        is.close();
    }

    /**
     * 测试添加用户
     */
    @Test
    public void testSaveUser() {
        User user = new User();
        user.setUsername("鱼开饭");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("广东");

        // 调用mapper完成添加
        int count = mapper.saveUser(user);
        System.out.println("添加条数为 : " + count);
    }
}
  • 这里有一点需要注意,如果在destory()方法中没有通过sqlSession.commit();来提交事务的话,那么添加的记录不会写入到数据库中。
  • 因为我们在调用openSession()时并没有设置事务自动提交,所以最终事务会自动回滚,导致记录没有写入到数据库中。见下图
    Mybatis学习笔记 - 02_第1张图片
  • openSession()的重载方法如下图,因此,如果需要自动提交事务,只需factory.openSession(true);即可。
    Mybatis学习笔记 - 02_第2张图片

1.4 测试结果

添加操作运行结果


2. 删除操作

2.1 在 UserMapper 接口中新增 removeUserById() 方法

/**
 * 根据id删除用户
 *
 * @param userId
 * @return 成功返回1,失败返回0
 */
int removeUserById(Integer userId);

2.2 在 Mapper 映射文件 UserMapper.xml 中配置删除操作


<delete id="removeUserById" parameterType="java.lang.Integer">
    DELETE FROM user WHERE id = #{uid}
delete>
  • 这里有两点需要注意一下!
  • 第一点,如果参数是基本类型或者基本类型的包装类,且只有一个参数,那么参数符号可以随便写 。也就是说,虽然 Mapper 接口中的方法声明为 int removeUserById(Integer userId) ,但是映射文件中既可以写 #{userId},也可以写 #{aaaa}
  • 第二点,parameterTypeintINTINTEGERintegerjava.lang.Integer都可以。原因可以看 6.2 typeAliases 标签

2.3 测试删除操作

/**
 * 测试删除用户
 */
@Test
public void testremoveUserById(){
    // 这里传的id为自己数据库中存在的id值
    int count = mapper.removeUserById(50);
    System.out.println("删除条数为 : " + count);
}

2.4 测试结果

删除操作执行结果


3. 修改操作

3.1 在 UserMapper 接口中新增 updateUser() 方法

/**
 * 修改用户
 * @param user
 * @return 成功返回1,失败返回0
 */
int updateUser(User user);

3.2 在 Mapper 映射文件 UserMapper.xml 中配置修改操作


<update id="updateUser" parameterType="cn.ykf.pojo.User">
    UPDATE user SET username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} WHERE id = #{id}
update>

3.3 测试修改操作

/**
 * 测试修改用户
 */
@Test
public void testUpdateUser() {
    // 因为还没有编写根据id查询用户,所以模拟数据
    User user = new User();
    user.setUsername("update");
    user.setAddress("test");
    user.setSex("女");
    user.setBirthday(new Date());
    // id 为自己数据库中存在的值
    user.setId(49);

    // 执行修改
    int count = mapper.updateUser(user);
    System.out.println("修改条数为 : " + count);
}

3.4 测试结果

修改操作执行结果


4. 查询操作

4.1 根据id查询

  • UserMapper 中添加方法
/**
 * 根据id查询单个用户
 * @param userId
 * @return
 */
User getUserById(Integer userId);
  • 修改映射文件

<select id="getUserById" parameterType="java.lang.Integer" resultType="cn.ykf.pojo.User">
    SELECT * FROM user WHERE id = #{uid}
select>
  • 测试代码及结果
/**
 * 测试查询单个用户
 */
@Test
public void testGetUserById() {
    // 确保id存在,否则返回null
    User user = mapper.getUserById(48);
    System.out.println(user);
}

查询单个用户结果

4.2 模糊查询

  • UserMapper 中添加方法
/**
 * 根据姓名模糊查询多个用户
 *
 * @param username
 * @return
 */
List<User> listUsersByName(String username);
  • 修改映射文件

 <select id="listUsersByName" parameterType="java.lang.String" resultType="cn.ykf.pojo.User">
     SELECT * FROM user WHERE username LIKE #{name}
 select>
  1. 虽然 UserMapper 中方法的返回值为 List,但是映射文件中 resultTypeUser就行。因为如果有多条记录的话,Mybatis 会自动帮我们封装成一个 List 集合。
  2. 这里的 parameterType 也可以直接写 String
  3. 参数符号可以随便写。
  • 测试代码及结果
/**
 * 测试模糊查询
 */
@Test
public void testListUsersByName() {
    List<User> users = mapper.listUsersByName("%王%");
    // 使用 Stream 流 + 方法引用,需要至少jdk8
    users.forEach(System.out::println);
}

Mybatis学习笔记 - 02_第3张图片

  • 如果出现了 There is no getter of 'name' in java.lang.string 的错误,请参考链接:mybatis中传入String类型参数的问题
  • 由于映射文件中的 SQL 语句并没有对参数进行模糊查询处理,所以在调用方法的时候我们必须手动为查询的关键字进行%拼接,这样很不方便。如果想调用方法查询时,只传入查询的关键字,那么可以采用以下方法 :
  1. 在 SQL 语句中,使用 MySQL 默认提供的函数 concat() 进行拼接
    SELECT * FROM user WHERE username LIKE concat('%',#{name},'%');
    
  2. 在 SQL 语句中手动进行拼接
    SELECT * FROM user WHERE username LIKE "%"#{name}"%";
    

4.3 使用聚合函数查询用户总数

  • UserMapper 中添加方法
/**
 * 查询用户总数
 *
 * @return
 */
int countUser();
  • 修改映射文件

<select id="countUser" resultType="int">
    SELECT count(id) FROM user
select>
  • 测试代码及运行结果
/**
 * 测试查询用户总数
 */
@Test
public void testCountUser() {
    int count = mapper.countUser();
    System.out.println("用户总记录数为 : " + count);
}

聚合函数查询结果


5. 扩展操作

5.1 添加用户时获取 id 的返回值

  • 在添加用户的时候,如果想获取新增用户的 id 值,那么就可以使用 标签,见代码

<insert id="saveUser" parameterType="cn.ykf.pojo.User">
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
        SELECT LAST_INSERT_ID()
    selectKey>
    INSERT INTO user(username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address})
insert>
  • keyProperty 表示 selectKey 语句结果应该被设置的目标属性(对应实体类)。
  • keyColumn 表示匹配属性的返回结果集中的列名称(对应数据库结果集)。
  • order 可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先生成主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后再执行 selectKey 中的语句。
  • 测试代码及运行结果
/**
 * 测试添加用户,并获取 id 的返回值
 */
@Test
public void testSaveUser() {
    User user = new User();
    user.setUsername("鱼开饭");
    user.setBirthday(new Date());
    user.setSex("男");
    user.setAddress("广东");

    System.out.println("添加前 : " + user);

    // 调用mapper完成添加
    int count = mapper.saveUser(user);
    System.out.println("添加条数为 : " + count);

    System.out.println("添加后 : " + user);
}

Mybatis学习笔记 - 02_第4张图片

5.2 使用 pojo 包装类进行查询

  • 首先介绍一下 OGNL 表达式
    • 全称 Object Graphic Navigation Language ,即对象图导航语言
    • 它是通过对象的取值方法来获取数据。在写法上把get给省略了。
  • 在开发中如果想实现复杂查询 ,查询条件不仅包括用户查询条件,还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用 pojo 包装对象传递输入参数
  • 编写 QueryVo 类来封装查询条件
/**
 * 用于封装查询条件
 *
 * @author yukaifan
 * @ClassName QueryVo
 * @date 2020/2/1 11:04
 */
public class QueryVo implements Serializable {
    private User user;
    // 如果还有其他的查询条件,就可以一并封装进来

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
  • 编写接口层代码和映射文件
/**
 * 根据查询条件模糊查询用户
 *
 * @param vo
 * @return
 */
List<User> listUsersByVo(QueryVo vo);

<select id="listUsersByVo" parameterType="cn.ykf.pojo.QueryVo" resultType="cn.ykf.pojo.User">
    SELECT * FROM user WHERE username LIKE CONCAT('%',#{user.username},'%')
select>
  • 测试代码及运行结果
/**
 * 测试根据Vo查询
 */
@Test
public void testListUsersByVo(){
    QueryVo vo = new QueryVo();
    User user = new User();
    user.setUsername("王");
    vo.setUser(user);

    List<User> users = mapper.listUsersByVo(vo);
    users.forEach(System.out::println);
}

根据Vo查询

5.3 使用 resultMap 接收查询结果

  • 在某些情况下,实体类的属性和数据库表的列名并不一致,那么就会出现查询结果无法封装进实体类。修改实体类进行演示:
public class User implements Serializable {
	// 此时实体类属性与数据库表的列表已经不一致了
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;

    // 此处不展示 getter()/setter()...

    // 此处不展示 toString()...
}
  • 修改映射文件中的参数符号,并测试原本的查询方法 listAllUsers() ,运行结果如下
    Mybatis学习笔记 - 02_第5张图片

原本应该所有的属性都为 null ,但是由于在 Windows 环境下 MySQL 不区分大小,所以 userName 等同 username ,不过要注意在 Linux 环境下 MySQL 严格区别大小写。

  • 为了解决实体类属性名与数据库表列名不一致,有以下解决方法:
  1. 在 SQL 语句中为所有列起别名,使别名与实体类属性名一致(执行效率相对高,开发效率低)
<select id="listAllUsers" resultType="cn.ykf.pojo.User">
    SELECT id AS userId, username AS userName, birthday AS userBirthday, sex AS userSex, address AS userAddress FROM user
select>
  1. 使用 resultMap 完成结果集到实体类的映射(执行效率相对低,开发效率高)
<mapper namespace="cn.ykf.mapper.UserMapper">
    
    <resultMap id="userMap" type="cn.ykf.pojo.User">
        <id property="userId" column="id" />
        <result property="userName" column="username"/>
        <result property="userBirthday" column="birthday"/>
        <result property="userAddress" column="address"/>
        <result property="userSex" column="sex"/>
    resultMap>
    
    <select id="listAllUsers" resultMap="userMap">
       SELECT * FROM user
    select>
mapper>

6. Mybatis 配置文件标签讲解

6.1 properties 标签

  • 在配置数据库连接的时候,我们可以采用以下几种方式来配置:
  1. 第一种,采用全局的内部配置。采用这种方式的话,如果需要配置多个数据库环境,那么像 username、password 等属性就可以复用,提高开发效率。


<configuration>
    
    <properties>
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_mybatis"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    properties>

    
    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC"/>
            
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            dataSource>
        environment>
    environments>

    
    <mappers>
        <mapper resource="UserMapper.xml"/>
    mappers>
configuration>
  1. 第二种,使用 resources 属性引入外部配置文件(常用)

编写配置文件 jdbcConfig.properties。配置文件名没有限制,但是配置文件一定要放在类路径下

# 键为 jdbc.xxx 可以自行修改
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_mybatis
jdbc.username=root
jdbc.password=123456

修改 Mybatis 配置文件


    <properties resource="jdbcConfig.properties"/>

    
    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC"/>
            
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            dataSource>
        environment>
    environments>
  1. 第三种,使用 url 属性引入外部配置文件

该方法的外部文件可以放在任意位置,但是路径写法必须按照 Url 的方式,写起来比较麻烦,推荐第二种方法


<properties url="file:///D:/document/IdeaProjects/java_web_ssm/my_mybatis/src/main/resources/jdbcConfig.properties"/>
  • URL:Uniform Resouce LOcator,即统一资源定位符。它可以唯一标识一个资源的位置,由四部分组成:协议、主机、端口、路径。
    • 例如:http://localhost:8080/mybatisserver/demo1,其中 http 为协议, localhost 为主机,8080 为端口号,/mybatisserver/demo1 为uri(路径)
  • URI:Uniform Resource Identifier,即统一资源标识符。它是在应用中可以唯一定位一个资源的。

6.2 typeAliases 标签

  • 之前在编写映射文件的时候, resultType 这个属性可以写 int、INT 等,就是因为 Mybatis 给这些类型起了别名。Mybatis 内置的别名如表格所示:
别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator
  • 如果我们也想给某个实体类指定别名的时候,就可以采用 typeAliases 标签。用法如下:
<configuration>
    
    <typeAliases>
        <typeAlias type="cn.ykf.pojo.User" alias="user"/>
    typeAliases>
    
configuration>
  • typeAlias 子标签用于配置别名。其中 type 属性用于指定要配置的类的全限定类名(该类只能是某个domain实体类), alias 属性指定别名。一旦指定了别名,那么别名就不再区分大小写。
  • 也就是说,此时我们可以在映射文件中这样写 resultType="user" ,也可以写 resultType="USER"
  • 当我们有多个实体类需要起别名的时候,那么我们就可以使用 package 标签。
<typeAliases>
    
    <package name="cn.ykf.pojo"/>
typeAliases>
  • package 标签指定要配置别名的包,当指定之后,该包下的所有实体类都会注册别名,并且别名就是类名,不再区分大小写
  • package 标签还可以将某个包内的映射器接口实现全部注册为映射器,如下所示

<mappers>
    <package name="cn.ykf.mapper"/>
mappers>
  • 这样配置后,我们就无需一个一个地配置 Mapper 接口了。不过,这种配置方式的前提是映射配置文件位置必须和dao接口的包结构相同,如图所示
    Mybatis学习笔记 - 02_第6张图片
  • 关于更多 XML 配置文件,请参考官方文档:Mybatis XML配置文件

你可能感兴趣的:(Mybatis,mybatis,数据库,java,mysql)