Mybatis学习笔记 - 02

Mybatis系列文章


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 中配置添加操作

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<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()时并没有设置事务自动提交,所以最终事务会自动回滚,导致记录没有写入到数据库中。见下图
    添加操作的回滚
  • openSession()的重载方法如下图,因此,如果需要自动提交事务,只需factory.openSession(true);即可。
    openSession()的重载

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<User>,但是映射文件中 resultTypeUser就行。因为如果有多条记录的话,Mybatis 会自动帮我们封装成一个 List 集合。
  2. 这里的 parameterType 也可以直接写 String
  3. 参数符号可以随便写。
  • 测试代码及结果
/**
 * 测试模糊查询
 */
@Test
public void testListUsersByName() {
    List<User> users = mapper.listUsersByName("%王%");
    // 使用 Stream 流 + 方法引用,需要至少jdk8
    users.forEach(System.out::println);
}

模糊查询多个用户结果

  • 如果出现了 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 值,那么就可以使用 <selectKey></selectKey> 标签,见代码
<!-- 添加用户,同时获取 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);
}

添加操作获取id

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() ,运行结果如下
    不使用resultMap查询

原本应该所有的属性都为 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 ,完成实体类与数据库表的映射 -->
    <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 等属性就可以复用,提高开发效率。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<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 内置的别名如表格所示:
别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator
  • 如果我们也想给某个实体类指定别名的时候,就可以采用 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接口的包结构相同,如图所示
    包结构相同
  • 关于更多 XML 配置文件,请参考官方文档:Mybatis XML配置文件
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值