Mybatis简单使用和详解

mybatis简单使用和详解

Java中操作数据库常用的持久层技术和持久层框架:

持久层技术主要有JDBC、JdbcTemplate和DBUtils,持久层框架主要有mybatis和hibernate。

1.JDBC技术:Connection、PreparedStatement、ResultSet。

2.Spring的JdbcTemplate:Spring中对Jdbc的简单封装。

3.Apache的DBUtils:它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装。

以上这些都不是框架(JDBC是规范、Spring的JdbcTemplate和Apache的DBUtils都只是工具类)。

mybatis是一个用Java编写的持久层框架,它使用ORM实现了结果集的封装。与hibernate一样,也是需要拥有两个配置文件:全局配置文件和映射文件,在编写这两个映射文件之前,必须创建mybatis环境(引入jar包或者maven工程导入坐标)。

ORM(Object Relational Mapping)是对象关系映射。简单来说,就是把数据库表和实体类及实体类的属性对应起来,让开发者操作实体类就实现操作数据库表。它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动、创建连接等烦杂过程。

1、mybatis简单使用

前提:安装mysql,创建数据库和数据表。

在windows系统中安装mysql之后,以管理员方式打开cmd窗口,通过net start mysql命令启动mysql服务,通过net stop mysql命令关闭mysql服务。

登录mysql:mysql -uroot -p

选择数据库(若无,则需要创建):use mytest

查询数据:

目录结构:

1、pom.xml文件引入依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mybatis-learn</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.2</version>
        </dependency>
        <!--<dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>-->
        <!-- 使用StringUtils.equalsAny()方法需要引入commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2、创建实体类

package com.learn.entity;

public class User {

    private Integer id;

    private String username;

    private Integer age;
    // 省略get、set方法
}

3、创建Mapper接口

package com.learn.mapper;

import com.learn.entity.User;

import java.util.List;

/**
 * @Author: 倚天照海
 */
public interface UserMapper {
    void createUser(User user);

    void deleteUserById(Integer userId);

    void updateUserById(User user);

    User queryUserById(Integer userId);

    User queryUserByIdWithResultType(Integer userId);

    String queryNameById(Integer userId);

    List<User> batchQueryByIds(List<Integer> ids);

    List<String> batchQueryNameByIds(List<Integer> ids);
}

4、创建Mapper.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="com.learn.mapper.UserMapper">
    <!--namespace的值必须是mapper接口的全限定名-->

    <resultMap id="baseResultMap" type="com.learn.entity.User">
        <result property="userId" column="user_id" jdbcType="INTEGER"/>
        <result property="username" column="username" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
    </resultMap>

    <sql id="columnInfo">
        user_id, username, age
    </sql>

    <insert id="createUser" parameterType="com.learn.entity.User">
        insert into t_user(user_id, username, age)
        values (#{userId}, #{username}, #{age})
    </insert>

    <delete id="deleteUserById">
        delete from t_user where user_id = #{userId}
    </delete>

    <update id="updateUserById" parameterType="com.learn.entity.User">
        update t_user
        <set>
            <if test="username != null">
                username = #{username},
            </if>
            <if test="age != null">
                age = #{age}
            </if>
        </set>
        <where>
            user_id = #{userId}
        </where>
    </update>

    <select id="queryUserById" parameterType="java.lang.Integer" resultMap="baseResultMap">
        select
        <include refid="columnInfo"/>
        from t_user
        <where>
            user_id = #{userId}
        </where>
    </select>
    <!-- 上面queryUserById中结果类型用的是resultMap映射,下面查询结果类型用的是resultType,
     在下面查询语句中需要使用别名将user_id映射到userId属性,否则mybatis查询到user_id字段无法给userId属性赋值。
     由于resultMap中已经做了映射,所以可以不用使用别名,如果表中字段与属性名称不一致,使用resultType必须用别名转换。-->
    <select id="queryUserByIdWithResultType" parameterType="java.lang.Integer" resultType="com.learn.entity.User">
        select
        user_id as userId, username, age
        from t_user
        <where>
            user_id = #{userId}
        </where>
    </select>

    <select id="queryNameById" parameterType="java.lang.Integer" resultType="java.lang.String">
        select username
        from t_user
        <where>
            user_id = #{userId}
        </where>
    </select>

    <!-- batchQueryByIds方法的返回值类型虽然是List<User>,但是resultType或resultMap对应的是List中元素的类型,即User -->
    <select id="batchQueryByIds" resultMap="baseResultMap">
        select
        <include refid="columnInfo"/>
        from t_user
        <where>
            user_id in
            <foreach collection="list" item="item" open="(" separator="," close=")">
                #{item}
            </foreach>
        </where>
    </select>

    <!-- batchQueryNameByIds方法的返回值虽然是List<String>,但是resultType不是List,而是List中元素的类型 -->
    <select id="batchQueryNameByIds" resultType="java.lang.String">
        select username
        from t_user
        <where>
            user_id in
            <foreach collection="list" item="userId" open="(" separator="," close=")">
                #{userId}
            </foreach>
        </where>
    </select>

</mapper>

5、创建mybatis配置文件MybatisConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>  <!--xml文件的一个声明-->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd"><!--xml文件的约束,来规定当前配置文件中都能使用哪些标签-->
<configuration>
    <!-- 设置别名,在xxMapper.xml文件中使用实体类时可以不用指定类的完全限定名,直接使用首字母小写的类名即可 -->
    <typeAliases>
        <package name="com.learn.entity"/>
    </typeAliases>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置Mysql的环境-->
        <environment id="mysql">
            <!--配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源(连接池)-->
            <dataSource type="POOLED">
                <!--配置连接数据库的四个基本信息-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mytest" />
                <property name="username" value="root" />
                <property name="password" value="root123" />
            </dataSource>
        </environment>
    </environments>
    <!--配置mapper映射文件-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
<!-- <mapper resource="mapper/*Mapper.xml" /> -->
    </mappers>

</configuration>

6、创建测试类进行增删改查

package com.learn;

import com.learn.entity.User;
import com.learn.mapper.UserMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Assert;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 倚天照海
 */
public class MybatisTest {
    public static void main(String[] args) {
        MybatisTest t = new MybatisTest();
        t.test();
    }

    private void test() {
        //mybatis配置文件
        String resource = "MybatisConfig.xml";
        InputStream inputStream;
        SqlSessionFactory sqlSessionFactory;
        SqlSession sqlSession = null;
        try {
            //根据配置文件获取输入流
            inputStream = Resources.getResourceAsStream(resource);
            //根据配置文件输入流创建会话工厂
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //根据会话工厂得到sqlsession
            sqlSession = sqlSessionFactory.openSession();
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 测试增删改查
            testInsertDeleteUpdateQuery(sqlSession, userMapper);
            // 测试批量查询
            testBatchQuery(sqlSession, userMapper);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭会话
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    private void testInsertDeleteUpdateQuery(SqlSession sqlSession, UserMapper userMapper) {
        // 创建用户
        createUser(sqlSession, userMapper);
        // 查询用户,验证是否创建成功
        User user1 = queryUserById(userMapper, 1);
        Assert.assertEquals("张三", user1.getUsername());
        // 根据用户id查询用户名称
        String username = userMapper.queryNameById(1);
        Assert.assertEquals("张三", username);
        // 修改用户
        updateUser(sqlSession, userMapper);
        // 查询用户,验证是否修改成功
        User user2 = queryUserById(userMapper, 1);
        Assert.assertEquals("李四", user2.getUsername());
        User user3 = userMapper.queryUserByIdWithResultType(1);
        Assert.assertEquals(user2.getUsername(), user3.getUsername());
        // 删除用户
        deleteUser(sqlSession, userMapper, 1);
        // 查询用户,验证是否删除成功
        User user4 = queryUserById(userMapper, 1);
        Assert.assertNull(user4);
    }

    private User buildUser(Integer id, String name, Integer age) {
        User user = new User();
        user.setUserId(id);
        user.setUsername(name);
        user.setAge(age);
        return user;
    }

    private void createUser(SqlSession sqlSession, UserMapper userMapper) {
        User user = buildUser(1, "张三", 10);
        userMapper.createUser(user);
        //提交事务,增删改需要commit,查询无需commit
        sqlSession.commit();
    }

    private void updateUser(SqlSession sqlSession, UserMapper userMapper) {
        User user = buildUser(1, "李四", 12);
        userMapper.updateUserById(user);
        //提交事务,增删改需要commit,查询无需commit
        sqlSession.commit();
    }

    private void deleteUser(SqlSession sqlSession, UserMapper userMapper, Integer userId) {
        userMapper.deleteUserById(userId);
        sqlSession.commit();
    }

    private User queryUserById(UserMapper userMapper, Integer userId) {
        //方式一:直接通过sqlsession的select方法查询
        //User user = sqlSession.selectOne("com.learn.mapper.UserMapper.queryUserById", 1);
        //方式二:通过mapper接口的方法查询(推荐使用第二种,该方式底层实际上依然是调用sqlsession的方法)
        return userMapper.queryUserById(userId);
    }

    private void testBatchQuery(SqlSession sqlSession, UserMapper userMapper) {
        createUser2(sqlSession, userMapper);
        List<User> users = userMapper.batchQueryByIds(Arrays.asList(5, 6));
        List<User> result = users.stream()
                .filter(user -> StringUtils.equalsAny(user.getUsername(), "王五", "赵六"))
                .collect(Collectors.toList());
        Assert.assertEquals(2, result.size());
        List<String> nameList = userMapper.batchQueryNameByIds(Arrays.asList(5, 6));
        Assert.assertEquals(2, nameList.size());
        System.out.println(nameList);
        deleteUser(sqlSession, userMapper, 5);
        deleteUser(sqlSession, userMapper, 6);
    }

    private void createUser2(SqlSession sqlSession, UserMapper userMapper) {
        User user1 = buildUser(5, "王五", 20);
        User user2 = buildUser(6, "赵六", 20);
        userMapper.createUser(user1);
        userMapper.createUser(user2);
        //提交事务,增删改需要commit,查询无需commit
        sqlSession.commit();
    }

}

2、mybatis详解

2.1、mybatis全局配置文件

mybatis的配置文件推荐为命名mybatis-config.xml,其内容包含了会深深影响mybatis行为的设置和属性信息。使用者掌握方面有properties、settings、typeAliases、enveronments、mappers。以下是全配置文件列表:

<?xml version="1.0" encoding="UTF-8" ?>  <!--xml文件的一个声明-->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd"><!--xml文件的约束,来规定当前配置文件中都能使用哪些标签-->
<!--配置-->
<configuration> 
	<!--属性-->
	<properties></properties> 
	<!--全局参数设置-->
	<settings></settings>     
	<!--类型别名-->
	<typeAliases></typeAliases>
	<!--类型处理器-->
	<typeHandles></typeHandles>
	<!--对象工厂-->
	<objectFactory></objectFactory>   
	<!--插件-->    
	<plugins></plugins>
	<!--环境信息集合:起步学习只需要关注一下此处配置--> 
	<environments>
		<!--单个环境信息-->  
	    <environment>
	    	<!--事务-->  
	        transactionManager
	        <!--数据源-->
	        dataSource
	    </environment>
	</environments>
	<!--数据库厂商标识-->
	databaseIdProvider
	<!--映射器-->
	<mappers></mappers>
</configuration>

具体各个配置内容如下:

1、properties 标签

开发者可通过properties属性来实现引用配置文件。这些属性都是可外部配置且可动态替换的。

[注]

1.如果两个配置文件有同一个字段,优先使用外部配置文件的

2.可以直接引入外部配置文件,properties子元素中可以增加一些属性配置

2、typeAliases 标签

typeAliases类型别名是为java类型设置一个短的名字,存在的意义仅在于用来减少类完全限定名的冗余。java内置内建类型别名它们都不区分大小写,注意对基本类型名称重复采用的特殊命名风格。

[注]

方法二中,每一个在包中的Java bean,在没有注解的情况下,会使用bean的首字母小写的非限定类名来作为它的别名。若有注解,则别名为其注解值。(实体类上使用注解:@Alias(“user”))

3、setting 标签

setting设置标签,这是Mybatis中极为重要的调整设置,它们会改变Mybatis的运行时行为。

4、environments标签

首先 看一段示例:

<!-- 配置mybatis的环境信息 -->
<environments default="development">
    <environment id="development">
        <!-- 配置JDBC事务控制,由mybatis进行管理 -->
        <transactionManager type="JDBC"></transactionManager>
        <!-- 配置数据源,采用dbcp连接池 -->
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/test_1?useUnicode=true&amp;characterEncoding=utf8"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </dataSource>
    </environment>
</environments>

dataSource子标签的type属性表达采用何种连接池方式, 在mybatis中连接池提供了3种方式的配置,在实际开发中都会使用连接池,因为它可以减少我们获取连接所消耗的时间。

①POOLED 方式:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现。下图中观察出POOLED它是从池中获取一个连接来用

②UNPOOLED 方式:采用传统的获取连接的方式,虽然也实现javax.sql.DataSource接口,但是并没有使用连接池技术。下图中观察出UNPOOLED每次创建一个新的连接来用。

③JNDI 方式:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样的,如果不是web或者maven的war工程,是不能使用的。

5、mappers标签

mappers映射器属性,MapperRegistry:注册绑定我们的Mapper文件。有三种方式如下:

<!-- 方式一 -->
<mappers>
	<mapper resource="com/learn/mapper/UserMapper.xml"/>
</mappers>	
<!-- 方式二 -->
<!--该方式绑定注册时,接口和它的Mapper配置文件必须同名,且必须在同一个包下-->
<mappers>
	<mapper class="com.learn.mapper.UserMapper" />
</mappers>
<!-- 方式三 -->
<!--该方式注入绑定时,接口和它的Mapper配置文件必须同名,且必须在同一个包下-->
<mappers>
	<package name="com.learn.mapper"></package>
</mappers>

2.2、mybatis映射文件

关于mybatis映射文件,主要从传递参数的方式、parameterType(输入类型)、resultType(输出类型)和resultMap(映射实体类)几个方面讲述。

2.2.1、传递参数的方式

在mapper中传递多个参数的方式有四种:顺序传递法、@Param注解传参法、Map传参法、Java Bean传参法。

方法1:顺序传参法

xxMapper.java:

public User selectUser(String name, int deptId);

xxMapper.xml:

<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{0} and dept_id = #{1}
</select>

#{}里面的数字代表传入参数的顺序。这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。

方法2:@Param注解传参法

xxMapper.java:

public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);

xxMapper.xml:

<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

#{}里面的名称对应的是注解@Param括号里面修饰的名称。这种方法在参数不多的情况还是比较直观的(推荐使用)。

方法3:Map传参法

xxMapper.java:

public User selectUser(Map<String, Object> params);

xxMapper.xml:

<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

#{}里面的名称对应的是Map里面的key名称。这种方法适合传递多个参数,且参数易变能灵活传递的情况。

方法4:Java Bean传参法

xxMapper.java:

public User selectUser(User user);

xxMapper.xml:

<select id="selectUser" parameterType="com.learn.pojo.User" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

#{}里面的名称对应的是User类里面的成员属性。这种方法直观,需要建一个实体类,扩展不容易,需要加属性,但代码可读性强,业务逻辑处理方便,推荐使用。

2.2.2、parameterType(输入类型)

xxMapper.java 可向 xxMapper.xml中输入三种类型:简单类型、pojo对象类型和pojo包装对象类型(即对象中嵌套对象)。

1.简单类型

如java.lang.Integer、java.lang.String等基本类型。

2.pojo对象类型

mybaits使用OGNL表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称。

[注]OGNL表达式:object Graphic Navigation Language (对象图导航语言),它是通过对象的取值方法来获取数据。在写法上把gat给省略了。比如:我们获取用户的名称类中的写法:user.getUsername(); OGNL表达式写法:user.username。

那么,mybatis中为什么能直接写username,而不用user呢?因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名。如下所示:

<select id="selectUser" parameterType="com.learn.pojo.User" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

3.pojo包装对象(对象中嵌套其他对象)

开发中通过pojo传递查询条件,查询条件是综合的查询条件,不仅包括用户查询条件还包括其他的查询条件(比如将用户购买商品消息也作为查询条件),这时可以使用包装对象传递输入pojo类参数中包含pojo。

2.2.3、resultType(输出类型)

可输出的类型有四种:返回一般数据类型(单条)、JavaBean 类型(单条)、List类型(多条)、Map类型。

1.一般数据类型(单条)

比如要根据Id属性获得数据库中的某个字段值,示例:

//**Mapper.java
String getStuNameById(Integer id);
<!--**Mapper.xml-->
<select id="getStuNameById" resultType="string">
	select username from t_student where id = #{id}
</select>

2. JavaBean 类型(单条)

比如根据某个字段获得数据库中的信息,把查询的结果信息封装成某个javaBean类型的数据,示例:

//**Mapper.java
Student getStuById(Integer id);
<!--**Mapper.xml-->
<select id="getStuById" resultType="student">
	select * from t_student where id = #{id}
</select>	

3. List类型(多条)

有时候开发者查询的数据不止一条,比如,模糊查询,全表查询等,这时候返回的数据可能不止一条数据,对于多数据的处理可以存放在List集合中,示例:

//**Mapper.java
List<Student> getAllStus();
<!--
**Mapper.xml
注意这里的 resultType 返回值类型是集合内存储数据的类型,不是 'list'
-->
<select id="getAllStus" resultType="student">
	select * from t_student
</select>

如果返回的类型是List<Map<String, Object>>:

//**Mapper.java
List<Map<String, Object>> getAllStuAsMap();
<!-- **Mapper.xml 查询多条-->
<select id="getAllStuAsMap" resultType="map">
	select * from t_student
</select>	

4. Map类型

mybatis支持将查询的数据封装成Map,如果查询的结果是一条,开发者可以把查询的数据以(表字段名,对应的值)方式存入到map中,示例:

//**Mapper.java
Map<String, Object> getStuAsMapById(Integer id);
<!-- 
**Mapper.xml
注意这里的 resultType 返回值类型是 'map'
-->
 <select id="getStuAsMapById" resultType="map">
	select * from t_student where id = #{id}
</select>

如果查询的结果是多条数据,我们也可以把查询的数据以{表中某一字段名, JavaBean}方式来封装成Map,通常是以主键或唯一键作为key,主键或唯一键对应的这条数据作为value。

①mapper(dao)接口:

// 查询所有学生的信息,把数据库中的'id'字段作为key,对应的value封装成Student对象
// @MapKey 中的值表示用数据库中的哪个字段名作 key
@MapKey("id")
Map<Integer, Student> getAllStusAsMap();

②Mapper.xml 映射文件:

<!--注意resultType返回值类型,不再是'map',而是Map的value对应的JavaBean类型-->
<select id="getAllStusAsMap" resultType="student">
	select * from t_student
</select>

2.2.4、resultMap(映射实体类)

实体类属性名和表中字段名不一样时,无法映射到值,输出为Null。这是因为mybatis会根据这些从数据库中查询到的列名,将列名转化为小写(数据库不区分大小写)去对应实体类中查询相应列名的set方法进行赋值,由于找不到setUserName(),所以会返回Null值。

解决方案有两种:

第1种:通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。

<select id="getOrder" parameterType="int" resultType="com.learn.pojo.Order">
       select order_id id, order_no orderno ,order_price price form t_orders where order_id=#{id};
</select>

第2种:通过resultMap 中的<result>来映射字段名和实体类属性名的一一对应关系。

<resultMap type="com.learn.pojo.Order" id="orderResultMap">
    <!–用id属性来映射主键字段–>
    <id property="id" column="order_id">
    <!–用result属性来映射非主键字段,property为实体类属性名,column为表中的字段名–>
 <result property ="orderno" column ="order_no"/>
 <result property="price" column="order_price" />
</reslutMap>

<select id="getOrder" parameterType="int" resultMap="orderResultMap">
 select * from t_orders where order_id=#{id}
</select>

<resultMap>标签的属性内容如下

<!-- resultMap标签的id属性表示mapper.xml文件中resultMap的唯一标识,在<select>查询标签中会用到,
 type属性表示将数据表映射成哪一个实体类,其值是实体类的完全限定名。-->
<resultMap id="" type="">
    <!-- id标签设置主键时使用(也可不用),用于将表中字段与实体类属性进行映射绑定,
     column属性表示表中字段名,jdbcType属性表示表中字段类型(可有可无),如INTEGER、VARCHAR
     property属性表示实体类中属性名称,javaType表示属性类型(可有可无),如Integer、String-->
    <id column="" jdbcType="" property="" javaType="" />
    <!-- result标签也是用于将表中字段与实体类属性进行映射绑定 -->
    <result column="" jdbcType="" property="" javaType=""/>
    
    <!-- 一般情况下一对一使用<association>标签,一对多使用<collection>标签 -->
    <!-- association标签表示两张表或两个类的关联关系,property属性表示一个类
    引用另一个类时定义的属性,javaType属性表示被引用类的全限定路径。详见下面示例 -->
    <association property="" javaType="">
        <id column="" jdbcType="" property=""/>
        <result  column="" jdbcType="" property=""/>
    </association>

    <!-- collection标签表示一对多的关联关系,在一个类中引用另一个类,
    被引用的类通常放在List集合中,如 private List<Book> books,
    property属性表示被引用类定义的属性,如books,ofType属性表示被引用类的全限定类名。 -->
    <collection property="" ofType="">
        <id column="" jdbcType="" property="" />
        <result column="" jdbcType="" property="" />
    </collection>
</resultMap>

2.2.5、association和collection标签

一般情况下一对一使用<association>标签,一对多使用<collection>标签

下面是<association>标签的一些属性:

property:映射实体类属性名

column:映射数据表字段名

javaType:映射java类型

jdbcType:映射数据库类型

select:用于加载复杂类型属性的映射语句的ID(它会从column 属性指定的列中检索数据,作为参数传递给目标 select 语句。)

resultMap:不使用嵌套模式,而是将此关联的嵌套结果集映射到一个外部的标签中,然后通过 id 进行引入。

typeHandler:数据库与Java类型匹配处理器,使用这个属性可以覆写类型处理器。

notNullColumn:不为空的列,如果指定了列,只有指定字段不为空时,Mybatis才会创建对象。

columnPrefix:给关联的数据库中的 column 添加一个前缀(如果不添加前缀,当两个表同时有 id 字段,查询结果时一定会产生覆盖,使得两个 id 的值一样)

resultSet:用于加载复杂类型的结果集名字。

foreignColumn:指定外键对应的列名,指定的列将与父类型中 column 的给出的列进行匹配。

autoMapping:自动封装,如果数据库字段和javaBean的字段名一样,可以使用这种方式;(但是不建议采取,如果非要使用此功能,那就在全局配置中加上mapUnderscoreToCamelCase=TRUE,它会使经典数据库字段命名规则翻译成javaBean的经典命名规则,如:a_column翻译成aColumn。)

fetchType:延迟加载,lazy打开延迟加载,eager积极加载。指定属性后,将在映射中忽略全局配置参数lazyLoadingEnabled,使用属性的值。

2.2.5.1、<association>

下面分别是使用<association>标签进行关联查询和嵌套查询的示例。

1、首先定义两个实体类:Book和Publisher

/**
*书籍
*/
@Data
public class Book {
    private String id;
    private String name;
    private String author;
    private Double price;
    //出版社,一本书对应一个出版社
    private Publisher pub; 
}


/**
*出版社
*/
@Data
public class Publisher {
    private String id;
    private String name;
    private String phone;
    private String address;
}

2、关联查询

<!--配置关联实体类-->
<resultMap id="bookResultMap" type="com.entity.Book">
    <!--主键属性-->
    <id property="id" column="id" jdbcType="VARCHAR"></id>
    <!--普通属性-->
    <result property="name" column="name" jdbcType="VARCHAR"></result>
    <result property="author" column="author" jdbcType="VARCHAR"></result>
    <result property="price" column="price" jdbcType="VARCHAR"></result>
<!--一对一映射-->
<!-- javaType="com.entity.Publisher"是在Book类中引用Publisher类的完全限定名,
property="pub"是在Book类中引用Publisher类时定义的属性名。-->
    <association property="pub" javaType="com.entity.Publisher">
        <id property="id" column="id" jdbcType="VARCHAR"></id>
        <result property="name" column="name" jdbcType="VARCHAR"></result>
        <result property="phone" column="phone" jdbcType="VARCHAR"></result>
        <result property="address" column="address" jdbcType="VARCHAR"></result>
    </association>
</resultMap>
<!--关联查询-->
<select id="selectAllBook" resultMap="bookResultMap">
    SELECT * FROM book e
    left JOIN publisher d ON e.publisher_id = d.id
</select>

3、嵌套查询

<resultMap id="bookResultMap" type="com.entity.Book">
    <!--主键属性-->
    <id property="id" column="id" jdbcType="VARCHAR"></id>
    <!--普通属性-->
    <result property="name" column="name" jdbcType="VARCHAR"></result>
    <result property="author" column="author" jdbcType="VARCHAR"></result>
<result property="price" column="price" jdbcType="VARCHAR"></result>
<!—association标签的column属性表示关联表的外键,property="pub"是在Book类中引用Publisher类时定义的属性名-->
    <association column="publisher_id" property="pub" 
        javaType="com.entity.Publisher" select="selectPublisher"></association>
</resultMap>
 
<!--出版社映射Map-->
<resultMap id="publisherResultMap" type="com.entity.Publisher">
    <id property="id" column="id" jdbcType="VARCHAR"></id>
    <result property="name" column="name" jdbcType="VARCHAR"></result>
    <result property="phone" column="phone" jdbcType="VARCHAR"></result>
    <result property="address" column="address" jdbcType="VARCHAR"></result>
</resultMap>
 
<!--书籍查询-->
<select id="selectAllBook" resultMap="bookResultMap">
    select * from book
</select>
 
<!--嵌套查询-->
<select id="selectPublisher" resultMap="publisherResultMap">
    SELECT * FROM publisher d
    WHERE d.id = #{publisher_id}
</select>

2.2.5.2、<collection>

<collection>和<association>标签属性基本相同,就多了一个ofType属性。ofType表示映射集合的类型(指定一对多的集合里面元素的类型)。

下面分别是使用<collection>标签进行关联查询和嵌套查询的示例。

1、首先定义两个实体类:Book和Publisher

/**
*书籍
*/
@Data
public class Book {
    private String id;
    private String name;
    private String author;
    private Double price;
}

/**
*出版社
*/
@Data
public class Publisher {
    private String id;
    private String name;
    private String phone;
    private String address;
    // 书籍列表,一个出版社对应多本书
    List<Book> bookList;
}

2、关联查询

<resultMap id="publisherResultMap" type="com.entity.Publisher">
    <id property="id" column="id" jdbcType="VARCHAR"></id>
    <result property="name" column="name" jdbcType="VARCHAR"></result>
    <result property="phone" column="phone" jdbcType="VARCHAR"></result>
<result property="address" column="address" jdbcType="VARCHAR"></result>
<!-- property="bookList"是在Publisher类中引用Book类时定义的属性名,ofType="com.entity.Book"是被引用类的全限定名。 -->
    <collection property="bookList" ofType="com.entity.Book">
        <id property="id" column="id" jdbcType="VARCHAR"></id>
        <result property="name" column="name" jdbcType="VARCHAR"></result>
        <result property="author" column="author" jdbcType="VARCHAR"></result>
        <result property="price" column="price" jdbcType="VARCHAR"></result>
    </collection>
</resultMap>
 
<select id="selectAllPublisher" resultMap="publisherResultMap">
    SELECT * FROM publisher d
    left JOIN book e ON e.publisher_id = d.id
</select>

3、嵌套查询

<resultMap id="publisherResultMap" type="com.entity.Publisher">
    <id property="id" column="id" jdbcType="VARCHAR"></id>
    <result property="name" column="name" jdbcType="VARCHAR"></result>
    <result property="phone" column="phone" jdbcType="VARCHAR"></result>
    <result property="address" column="address" jdbcType="VARCHAR"></result>
    <collection column="id" property="bookList" 
        javaType="java.util.ArrayList" ofType="com.entity.Book"
       select="selectBookList"/>
</resultMap>
 
<resultMap id="bookResultMap" type="com.worldly.config.entity.Employee">
    <id property="id" column="id" jdbcType="VARCHAR"></id>
    <result property="name" column="name" jdbcType="VARCHAR"></result>
    <result property="author" column="author" jdbcType="VARCHAR"></result>
    <result property="price" column="price" jdbcType="VARCHAR"></result>
</resultMap>
 
<select id="selectAllPublisher" resultMap="publisherResultMap">
    SELECT * FROM publisher d
</select>
 
<select id="selectBookList" resultMap="bookResultMap">
    SELECT * FROM book e
    WHERE e.publisher_id = #{id}
</select>

2.2.6、jdbcType与javaType对照表

jdbcType

javaType

jdbcType

javaType

CHAR

String

DOUBLE

double

VARCHAR

String

BINARY

byte[]

LONGVARCHAR

String

VARBINARY

byte[]

NUMERIC

java.math.BigDecimal

LONGVARBINARY

byte[]

DECIMAL

java.math.BigDecimal

DATE

java.sql.Date

BIT

boolean

TIME

java.sql.Time

BOOLEAN

boolean

TIMESTAMP

java.sql.Timestamp

TINYINT

byte

CLOB

Clob

SMALLINT

short

BLOB

Blob

INTEGER

INTEGER

ARRAY

Array

BIGINT

long

DISTINCT

mapping of underlying type

REAL

float

STRUCT

Struct

FLOAT

double

REF

Ref

DATALINK

java.net.URL

2.2.7、mybatis动态SQL

mybatis动态SQL是指使用<if>、<where>、<set>、<foreach>等标签拼接的SQL语句。

1、if标签语句示例:

if标签会执行所有的if标签,并进行条件判断,满足的部分进行sql拼接。

<select id="queryUser" parameterType="map" resultType="user">      
	  select * from usr where 1=1
	  <if test="id != null" >      
	      and id =#{id}  
	  </if>
	  <if test="username != null" >
	      and username = #{username}
	  </if>
</select>

2、where标签语句示例:

动态拼接where(并且会智能的去掉第一个语句的and或者or)。

<!-- where 元素只会在至少有一个子元素的条件返回sql子句的情况下,才去插入"where" 子句-->
<select id="queryUser" parameterType="map" resultType="user">   
	  select * from usr  
		  <where>
			  <if test="id != null" >
			      id =#{id}
			  </if>
			  <if test="username != null" >
			      and username = #{username}
			  </if>
		  </where>
</select>

3、choose,when,otherwise标签语句示例:

不会执行所有的when语句,遇到满足的条件,后面的when语句不会执行,otherwise可以不写。

注意:每个 <choose> 标签内部的 <when> 和 <otherwise> 子元素都是互斥的,只会选择满足条件的第一个子元素。如果需要对多个字段进行选择处理,可以在 SQL 语句中多次使用 <choose> 标签,每个 <choose> 标签对应一个字段的选择逻辑。

<!-- 有时不想应用到所有的条件语句,而只想从中择其一项,针对这种情况,Mybatis提供了choose元素,它有点像java中的switch语句 -->
<select id="queryUser" parameterType="map" resultType="user">
	  select * from usr
	  <where>
	      <choose>
	          <when test="id != null" >
	              id =#{id}
	          </when>
	          <when test="username != null" >
	              and username = #{username}
	          </when>
	          <otherwise>
	              and id= 4
	          </otherwise>
	      </choose>
	  </where>
</select>

4、set 标签语句示例:

用于更新操作(并且会智能的去掉最后一个语句后面的逗号)。

<!-- 这里set元素会动态前置set关键字,同时也会删除掉无关的逗号,因为用了条件语句之后很可能就会生成的sql后面留下这些逗号 -->
<update id="updatePwd">
    update t_user
    <set>
        <if test="name !=null">name=#{name},</if>
        <if test="pwd !=null">pwd=#{pwd},</if>
    </set>
    <where>
        <choose>
            <when test="id!=null">id=#{id}</when>
            <when test="name!=null">name=#{name}</when>
        </choose>
    </where>
</update>	

5、foreach 标签语句示例:

collection:表示迭代集合的名称,可以使用@Param注解指定,如下所示,该参数为必选。

item:表示本次迭代获取的元素,若collection为List、Set或者数组,则表示其中的元素; 若collection为map,则代表key-value的value,该参数为必选。

open:表示该语句以什么开始,最常用的是左括号’(’,注意:mybatis会将该字符拼接到整体的sql语句之前,并且只拼接一次,该参数为可选项。

close:表示该语句以什么结束,最常用的是右括号’)’,注意:mybatis会将该字符拼接到整体的sql语句之后,该参数为可选项。

separator:表示在每次迭代后给sql语句追加上separator属性指定的字符,该参数为可选项。

index:在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项。

<!--
第一步:迭代集合,获取对应的item,和外部的(),拼接形成('zhangsan')
第二步:在之前的基础上拼接上逗号分隔符('zhangsan'),
第三步:继续迭代并拼接逗号 ('zhangsan'),('lisi'),
第四步:继续迭代并拼接逗号 ('zhangsan'),('lisi'),('wangwu')
 -->
<foreach collection="list" item="item" separator=",">
	(#{item})
</foreach>

<!--
第一步:拼接open指定的开始字符 (
第二步:迭代集合,拼接对应的item, ('zhangsan'
第三步:拼接separator指定的分隔符 ('zhangsan',
第四步:迭代集合,拼接对应的item, ('zhangsan','lisi'
第五步:拼接separator指定的分隔符('zhangsan','lisi',
第六步:拼接close指定的闭合字符  ('zhangsan','lisi','wangwu') 
 -->
<foreach collection="list" item="item" open="(" separator="," close=")">
	#{item}
</foreach>

6、sql片段标签语句示例:

在mapper.xml中有些sql片段,在多个sql语句中都有用,每个都写的话显得代码冗余,可以用到代码片段,将公共部分抽取出来。

注意点:最好基于单表定义sql片段,并且sql片段中不要存在where标签,因为where标签会自动优化sql中的and和or。

<sql id="choose-when-id-name">
    <choose>
        <when test="id!=null">id=#{id}</when>
        <when test="name!=null">name=#{name}</when>
    </choose>
</sql>

<update id="updatePwd">
    update t_user
    <set>
        <if test="name !=null">name=#{name},</if>
        <if test="pwd !=null">pwd=#{pwd},</if>
    </set>
    <where>
       <include refid="choose-when-id-name"></include>
    </where>
</update>

7、trim标签语句示例:

trim标记是一个格式化的标记,可以完成set或者是where标记的功能。

①用 trim 改写上面的 if+where 语句

prefix:前缀

prefixoverride:去掉第一个and或者or。

<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.java01.entity.User">
    select * from user
    <!-- <where>
            <if test="username != null">
               username=#{username}
            </if>

            <if test="sex != null">
               and sex=#{sex}
            </if>
        </where>  -->
    <trim prefix="where" prefixOverrides="and | or">
        <if test="username != null">
            and username=#{username}
        </if>
        <if test="sex != null">
            and sex=#{sex}
        </if>
    </trim>
</select>

②用 trim 改写上面的 if+set 语句

suffix:后缀

suffixoverride:去掉最后一个逗号(也可以是其他的标记,就像是上面前缀中的and一样)。

<!-- 根据 id 更新 user 表的数据 -->
<update id="updateUserById" parameterType="com.ys.po.User">
    update user u
        <!-- <set>
                <if test="username != null and username != ''">
                    u.username = #{username},
                </if>
                <if test="sex != null and sex != ''">
                    u.sex = #{sex}
                </if>
            </set> -->
    <trim suffix="set" suffixOverrides=",">
        <if test="username != null and username != ''">
            u.username = #{username},
        </if>
        <if test="sex != null and sex != ''">
            u.sex = #{sex},
        </if>
    </trim>
    where id=#{id}
</update>

3、mybatis分页

mybatis分页可以减少数据的处理量,分页的方式有两种:limit分页、PageHelper分页插件。

1、limit分页方式

①编写xxMapper.java:

List<User> getUserByLimit(Map<String,Integer> data);

②编写xxMapper.xml:

<select id="getUserByLimit" parameterType="map" resultType="user">
	select * from usr limit #{currIndex},#{pageSize}
</select>

③编写测试service层:

//接口
List<Usr> queryUserBySql(int currPage, int pageSize);

//实现类
public List<Student> queryUserBySql(int currPage, int pageSize) {
	Map<String, Object> data = new HashedMap();
    data.put("currIndex", (currPage-1)*pageSize);
    data.put("pageSize", pageSize);
    return xxMapper.getUserByLimit(data);
}

2、PageHelper分页插件

导入maven坐标(由于使用了sql 解析工具,所以maven也引入了 jsqlparser.jar)

<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
	<version>4.0.0</version>
</dependency>

<!-- 配置mybatis-config.xml拦截器插件 -->
<plugins>		
	<plugin interceptor="com.github.pagehelper.PageHelper">	    
		<property name="dialect" value="mysql"/>	        
	</plugin>    
</plugins>

[注]配置拦截器时要知道新版拦截器是 com.github.pagehelper.PageInterceptor,而com.github.pagehelper.PageHelper 现在是一个特殊的 dialect 实现类,是分页插件的默认实现类,提供了和以前相同的用法。

设置数据库类型时有 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL 六种数据库。

dialect默认情况下会使用 PageHelper 方式进行分页。

通过 MyBatis PageHelper 进行分页查询实际上非常简单,只需在service(或mapper)方法执行查询前,调用一次 PageHelper.startPage(pageNum,pageSize) 来设置分页查询参数即可,其中pageNum 为记录页数,pageSize 为单页记录数量。此时service(或mapper)方法的查询结果就是分页后的结果了。如果期望获得相关的分页信息,还可以将查询结果封装到PageInfo对象中,以获得总页数、总记录数、当前页数等相关分页信息。

/**
 * 分页查询
 * @param pageNum 记录页数
 * @param pageSize 单页记录数量
 * @return
 */
@ResponseBody
@RequestMapping("/findPage")
public List<User> findPage(@RequestParam int pageNum, @RequestParam int pageSize) {
    // 设置分页查询参数
    PageHelper.startPage(pageNum,pageSize);
    List<User> userList = userService.findAll();

    for(User user : userList) {
        System.out.println("element : " + user);
    }

    // 封装分页查询结果到 PageInfo 对象中以获取相关分页信息
    PageInfo pageInfo = new PageInfo(userList);
    System.out.println("总页数: " + pageInfo.getPages());
    System.out.println("总记录数: " + pageInfo.getTotal());
    System.out.println("当前页数: " + pageInfo.getPageNum());
    System.out.println("当前页面记录数量: " + pageInfo.getSize());

    return pageInfo.getList();
}

[注]

a.只有紧跟在PageHelper.startPage方法后的第一个Mybatis的查询(Select)方法会被分页

b.对于带有for update的sql,会抛出运行时异常,对于这样的sql建议手动分页,毕竟这样的sql需要重视。

c.分页插件不支持嵌套结果映射,由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。

PageHelper.startPage(pageNum,pageSize) 只对其后的第一次SQL查询进行分页。故若需进行分页查询,必须每次在service(或mapper)方法执行SQL查询前调用PageHelper.startPage(pageNum,pageSize) 方法。

  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值