MyBatis环境搭建、CRUD、动态SQL、ORM映射、多表连接、主键回填

MyBatis

一、Mybatis概述

1.1 什么是ORM框架?

ORM(Object Relational Mapping)对象关系映射,将程序中的一个对象与表中的一行数据一一对应。ORM框架提供了持久化类与表的映射关系,在运行时参照映射文件的信息,把对象持久化到数据库中。

1.2 maven搭建MyBatis注意事项

IDEA关联maven有时会失效,需要先确认一下关联maven,是否正确。

打开settings/maven/检查maven默认版本、maven默认配置文件、maven默认仓库地址。

二、在maven中搭建MyBatis环境

2.1 配置pom.xml文件

在maven项目的pom.xml配置文件的标签中添加即可。

		<!--MyBatis核心依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>

		<!--MySql驱动依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

		<!--junit测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>

在标签中加入如下代码解决Mapper文件读取问题。

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <!-- 默认(新添加自定义则失效) -->
              	<include>*.xml</include>
                <!-- 新添加 */代表1级目录 **/代表多级目录 -->
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

2.2 创建jdbc.properties配置文件

在项目目录的src\main\resources文件夹下创建jdbc.properties文件。

#jdbc.properties,根据具体项目修改。
#Mysql5的driver地址如下,MySQL6的driver地址为com.mysql.cj.jdbc.Driver
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库名?characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai
username=root
password=123456

2.3 创建MyBatis配置文件

2.3.1 创建mybatis-config.xml

在项目目录的src\main\resources文件夹下创建mybatis-config.xml文件。

<?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>
    <!--这是一个空模板-->
</configuration>

在标签中加入配置。

<!--MyBatis配置-->
    <!--引入外部properties文件地址-->
    <properties resource="jdbc.properties"></properties>
    
    <!--JDBC环境配置、选中默认环境-->
    <environments default="MySqlDB">
    	<!--MySql数据库环境配置-->
    	<environment id="MySqlDB">
    		<!--事务管理-->
    		<transactionManager type="JDBC"/>
    		<!--连接池-->
    		<dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">
       			<!--引入步骤二中jdbc.properties配置文件的信息-->
        		<property name="driver" value="${driver}"/>
     			<property name="url" value="${url}"/>
        		<property name="username" value="${username}"/>
        		<property name="password" value="${password}"/>   
    		</dataSource>
        </environment>
    </environments>
2.3.2 mybatis-config配置结构

在配置mybatis-config.xml的时候,必须按照下面的顺序进行配置。文档的顶层结构如下:

configuration 配置

​ properties 属性

​ settings 设置

​ typeAliases 类型命名

​ typeHandlers 类型处理器

​ objectFactory 对象工厂

​ plugins 插件

​ environments 环境

​ environment 环境变量

​ transactionManager 事务管理器

​ dataSource 数据源

​ databaseIdProvider 数据库厂商标识

​ mappers 映射器

2.4 编写Mapper.xml代替daoImpl

<?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">

<!--namespace = 所需实现的接口全限定名-->
<mapper namespace="com.qf.mybatis.part1.basic.UserDao">
    
    <!--编写一个SQL用于测试-->
    <!--id = 所需重写的接口抽象方法,resultType = 查询后所需返回的对象类型-->
    <select id="selectUserById" resultType="com.qf.mybatis.part1.basic.User">
      	SELECT * FROM t_users
    </select>
    
</mapper>

2.5 将Mapper.xml注册到mybatis-config.xml中

添加到标签中。

	<!--Mapper注册-->
    <mappers>
        <!--注册Mapper文件的所在位置-->
        <!--注意,在创建xxxMapper.xml时,不能使用包名.包名...的方式,要使用包名/包名/...-->
        <mapper resource="包名/xxxMapper.xml"></mapper>
    </mappers>

2.6 测试

编写完善dao、数据库、entity。

在src/test/java中编写测试类。

使用@Test注解编写测试方法。

	@Test
    public void test1() throws IOException {
		//1.获得读取MyBatis配置文件的流对象
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); 
		//2.构建SqlSession连接对象的工厂
    	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
    	//3.通过工厂获得连接对象
   	 	SqlSession sqlSession = factory.openSession();

        //1-3步骤固定
   		//4.通过连接对象获得接口实现类对象  
        UserDao userDao = sqlSession.getMapper(UserDao.class);
    	//5.调用接口中的方法
    	for(User user : userDao.getAllUser()){
            System.out.println(user);
        };
        //曾删改需要加上	sqlSession.commit(true);手动提交,查询不用加
	}

注意:若查询到的数据为null,有可能是数据库列名与实体类列名不相同,导致ORM自动映射失效,之后部分有映射详解。

三、MyBatis的增删改查操作

3.1 增删改的手动提交

增、删、改方法在调用时需要手动提交。查询不用。

sqlSession.commit(true);

3.2 参数绑定与简单查询

3.2.1 查询标签
<!--select标签为查询标签-->
< select id="dao中的方法名" resultType="返回值类型,查询方法一般返回对象或对象的集合,这里的返回值类型为,包名.对象所在的类名" >
    SELECT * FROM t_users WHERE id = #{id}
</select>
3.2.2 参数绑定

当sql语句只有一个参数时,参数不需要绑定。只需要在sql语句中需要参数的地方 #{dao层中的形参名称} 即可。

3.2.2.1 注解参数绑定

dao

//导包,导入注解
import org.apache.ibatis.annotations.Param; 

public interface UserDao {
	//示例
    User getUserByNameAndPwdOther (@Param("userName") String userName, @Param("userPwd") String userPwd);
}

mapper中参数绑定方式

<select id="getUserByNameAndPwdOther" resultType="entity.User">
    SELECT * FROM user where
    <!--使用@Param("userName")中的值进行绑定-->
    userName = #{userName} and userPwd = #{userPwd}
</select>
3.2.2.2 对象参数绑定

dao

User getUserByObject(User user);

mapper中参数绑定方式

<select id="getUserByObject" resultType="entity.User">
    SELECT * FROM user
    <!--方法参数为对象时,可直接使用#{属性名}进行获取-->
    <!--#{userName}这里取值是dao层参数user对象中的属性值-->
    WHERE userName = #{userName} AND userPwd = #{userPwd} 
</select>
3.2.2.3 序号参数绑定

dao

User getUserByNameAndPwd(String userName, String userPwd);

mapper中参数绑定方式

<select id="getUserByNameAndPwd" resultType="entity.User">
    SELECT * FROM user
    WHERE userName = #{arg0} AND userPwd = #{arg1} 
    <!--arg0 arg1 arg2 ...-->
    <!--或者将arg改为	param1 param2 param3 ...-->
</select>
3.2.2.4 Map参数绑定

dao

User getUserByNameAndPwd(String userName, String userPwd);

测试类添加Map集合

Map values = new HashMap();
//V为前端页面传入的数据
values.put("userName","盖伦");
values.put("userPwd","1");
//调用方法
User user = userDao.getUserByMap(values);

mapper中参数绑定方式

<select id="getUserByMap" resultType="entity.User">
    SELECT * FROM user where
    <!--#{userName}这里直接写Map中的K即可-->
    userName = #{userName} and userPwd = #{userPwd}
</select>

3.3 模糊查询

dao

List<User> getUserByNameLike(@Param("userName") String userName);

mapper中使用concat()函数添加条件。

<select id="getUserByNameLike" resultType="entity.User">
    SELECT * FROM user 
    where userName
    like concat('%', #{userName},'%')
</select>

3.4 添加与主键

3.4.1 自动主键与主键回填

数据库中主键设为自增,在添加用户时,即使User对象的id设为null,添加成功时数据库中的该数据的id也会自增补全。

此时我们无法得知数据库自动补全的id,所以需要主键回填。

注意:1、使用自动主键时,只适用于整数类型自增主键,数据类型一般为Integer。

​ 2、调用添加SQL的方法,必须传入一个对象,否则回填的主键(一般都是id)会报无法接收异常。

dao

Integer addUser(User user);

mapper

<insert id="addUser" parameterType="entity.User">
    
    insert into user values
    <!--第一个参数为userId,若传入的User对象的userId为null,则#{userId}也为null,添加成功后,userId会自增补全。直接在sql里填写null也可以有一样的效果-->
    (#{userId}, #{userName}, null, null, null, null, null, null)
    
    <!--如果添加了一个引用数据类型,如User类中的Car car,而数据库user表中只有car_id列-->
	<!--那么values中的数据应该这样传(#{car.carId}, ...),注意car首字母小写-->
    
    <!--以下为主键回填部分,keyProperty设置主键,order="AFTER"在添加后回填-->
    <selectKey keyProperty="userId" resultType="int" order="AFTER">
            SELECT LAST_INSERT_ID()
    </selectKey>
</insert>

<!--也可以在<insert>标签中添加useGeneratedKeys="true"和keyProperty="id"实现此功能,效果相同-->

效果演示

//初始化对象,对名字赋值,此时id为null
User user = new User("嘉文一世");
//调用添加人员的addUser方法
int i = userDao.addUser(user);
//提交
sqlSession.commit(true);

//因为在sql语句中设置了主键回填,此时user对象中已经有id的值了,若不设置,则id值为0。
System.out.println(user);
//打印效果	User{userId=35, userName='嘉文一世'}
3.4.2 手动主键与UUID

与自动主键相同,但需要额外设置传入的User对象的userId。为了保证userId不重复可以使用UUID生成一个随机不重复的36位字符串,UUID中间有4个”-“分隔符,有效位数32位。

注意:使用UUID生成主键时,主键数据类型为String。主键长度为32。

<insert id="addUser" parameterType="entity.User">
    insert into user values(#{userId}, #{userName}, null, null, null, null, null, null)
    
    <!--以下为主键回填部分,keyProperty设置主键,order="BEFORE"在添加前回填-->
    <selectKey keyProperty="userId" resultType="String" order="AFTER">
        <!--SELECT uuid()查询uuid,replace切割字符串方法,目的是去掉uuid中的'-'-->
        SELECT replace(uuid(), '-', '')
    </selectKey>
</insert>

效果与4.1相同。

3.5 删除

mapper

<!--根据ID删,返回受影响行数-->
<delete id="deleteUserById" parameterType="int">
    DELETE FROM user
    <!--只有一个参数时,#{任意书写}-->
    WHERE userId = #{userId} 
</delete>

3.6 修改

mapper

<!--传入对象修改-->
<update id="dao中的方法名" parameterType="dao层方法传入参数的类型">
    UPDATE t_users 
    SET name=#{name}, password=#{password}
    WHERE id = #{id} <!--方法参数为对象时,可直接使用#{属性名}进行获取-->
</update>

四、ORM映射

使用MyBatis时,查询到的rs结果集中的数据不需要再一一取出并添加到对象中,这就是ORM映射。

MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系,即局部映射,二者不同时,无法自动ORM,需要手动创建全局映射。

4.1 MyBatis映射的三种级别

级别含义说明
PARTIAL局部映射默认的映射级别,映射与数据库以对应的实体类。不能映射结果集,如实体类中的对象或集合。
NONE不映射全部手动映射,一般不用。
FULL全局映射可以自定义映射的结果集,如Stu中的Grade grade属性。

在mybatis-config.xml文件的标签下方设置级别。

<!--设置标签必须位于<properties>标签下方-->
<settings>
    <!--设置属性名(自动映射行为),值默认PARTIAL,三个级别都是静态常量-->
	<setting name="autoMappingBehavior" value="PARTIAL"/>
</settings>

4.2 使用as添加列别名

在SQL中使用 as 为查询字段添加列别名,以匹配属性名。但此种方式不能处理实体类中存在结果集的情况。

SELECT
user_id AS id , user_name AS name , user_pwd AS pwd 
FROM user
WHERE user_id = #{id}

4.3 通过resultMap映射匹配列名与属性名

在mapper.xml文件中的标签中添加标签。

<!--定义resultMap标签-->
<resultMap id="映射ID" type="映射实体类全限定名(包名.类名)">
    
    <!--关联主键与列名,id标签只能写一个,对应数据表中的主键列,property实体类属性,column数据库列名-->
    <id property="gradeId" column="grade_id" />
    <!--关联属性与列名-->
    <!--result标签可以写任意个,对应数据表中的非主键列-->
    <!--如果无法自动动映射,又没有在resultMap中添加映射,那么就无法为数据库的这一列进行CRUD操作,查询出的值为null。-->
    <result property="gradeName" column="grade_name"/>
</resultMap>

在mapper.xml文件中使用resultMap作为ORM映射依据。

<!--这里不使用resultType,而替换成resultMap-->
<select id="getAllGrade" resultMap="映射ID">
        select * from grade
</select>

五、Mybtis处理多表连接

5.1 OneToOne

例:Stu类中有House house属性,

​ House类中有id、name属性,

​ stu表中有int类型的stu_house列。

要求查询结果为:stu表中所有数据,且stu_house显示为House.name。

<resultMap id="StuRM" type="com.qf.pojo.Stu">
        <id column="stu_id" property="stuId"></id>
        <result column="stu_name" property="stuName"></result>
        <result column="stu_pwd" property="stuPwd"></result>
        <result column="stu_car_id" property="stuCarId"></result>
    
    	<!--使用<association>标签为Stu类中的嵌套类-House进行查询操作-->
        <association property="house" javaType="com.qf.pojo.House">
            <id column="id" property="id"></id>
            <!--这里的<result>标签可以不用添加,设置Mybatis的映射级别为FULL即可。
			但是,若House类中仍有对象或集合,尚不清楚有无BUG。
			这一操作只能在<association>标签中使用-->
            <result column="name" property="name"></result>
     	</association>
</resultMap>


<!--SQL-->
<select id="selectAllStu" resultType="com.qf.pojo.Stu" resultMap="StuRM">
   SELECT * FROM stu, house where stu.stu_house = house.id
</select>

打印结果。

Stu{stuId=1, stuName=‘伊芙琳’, stuPwd=‘111’, stuCarId=1, house=House{id=1, name=‘小楼’}}

5.2 OneToMore

例:Grade类中有List stuList属性;

​ grade表中有id、name字段;

​ Stu类中有Integer gradeId属性;

​ stu表中有stu_gradeId字段。

要求查询:按班级查询出对应的所有学生。

<resultMap id="GradeRM" type="com.qf.pojo.Grade">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <!--使用<collection>标签映射集合-->
    <collection property="stuList" ofType="com.qf.pojo.Stu">
        <id column="stu_id" property="stuId"></id>
        <result column="stu_id" property="stuId"></result>
        <result column="stu_name" property="stuName"></result>
        <result column="stu_pwd" property="stuPwd"></result>
        <result column="stu_car_id" property="stuCarId"></result>
        <!--因为映射的类Stu中存在对象House所以还要使用<association>再次映射-->
        <association property="house" javaType="com.qf.pojo.House">
            <id column="id" property="id"></id>
        </association>
    </collection>
</resultMap>


<!--SQL-->
<select id="selectGradeById" resultMap="GradeRM">
    select * from stu, grade 
   	where stu.stu_gradeId = grade.id 
    and grade.id = 1
</select>

打印结果:

Grade{id=1, name=‘德玛西亚’, stuList=[

​ Stu{stuId=1, stuName=‘伊芙琳’, stuPwd=‘111’, stuCarId=1, house=House{id=1, name=‘德玛西亚’}}, Stu{stuId=2, stuName=‘娜美’, stuPwd=‘111’, stuCarId=2, house=House{id=1, name=‘德玛西亚’}}]}

5.3 MoreToMore

实现需求:一个学生可以选多门课程,一门课程可以被多个课程选择。

​ 将学生与所选择的课查询出来。

实体类:

//课程类
public class Subject {
    private int subjectId;
    private String subjectName;
    private List<Stu> stuList;
}

//学生类
public class Stu {
    private Integer stuId;
    private String stuName;
    private List<Subject> subjectList;
}

新建一张中间表关联subjectId与stuId作为联合主键。

表名:sub_stu

列名:sub_stu_id、subjectId、stuId

mapper映射:

<resultMap id="stuAndSub" type="com.qf.pojo.Stu">
    <id column="stu_id" property="stuId"></id>
    <result column="stu_name" property="stuName"></result>
    <collection property="subject" ofType="com.qf.pojo.Subject">
        <id column="subId" property="subId"></id>
        <result column="subName" property="subName"></result>
    </collection>
</resultMap>

SQL语句:

SELECT stu.stu_name, sub.subName
from stu
INNER JOIN sub_stu
on stu.stu_id = sub_stu.stuId
INNER JOIN sub
on sub_stu.subId = sub.subId

查询结果:

stuname subname

伊芙琳 数学
伊芙琳 语文
娜美 语文
崔丝塔娜 数学

六、动态SQL

MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果。

6.1 引用SQL片段

	<!-- 定义SQL片段 -->    
	<sql id="STU_FIELD">
        SELECT id,name
    </sql>

    <select id="selectStu" resultType="com.qf.pojo.Stu">
    	<!-- 通过ID引用SQL片段,来达到拼接SQL的效果 -->		
        <include refid="STU_FIELD" />
        FROM t_books
    </select>

6.2 where条件查询

<select id="selectStu" resultType="com.qf.pojo.Stu">
    SELECT id, name, age
    FROM stu
    <where> 
        <!-- WHERE标签,会自动忽略前后缀(如:and | or),但是其中的字标签if不可以忽略前后缀-->
        <!--当if判断需要多条件时,使用and拼接-->
        <if test="id != null and id != ''"> 
            id = #{id}
        </if>
        <if test="name != null and id != ''">
            and name = #{name}
        </if>
        <if test="age != null and id != ''">
            and author = #{author}
        </if>
    </where>
</select>

6.3 set修改

<update id="updateStuById">
    UPDATE stu
    <set>
        <!-- where子句中满足条件的if,会自动忽略后缀(如:,) -->
        <if test="name != null">
            name = #{name} ,
        </if>

        <if test="age != null">
            age = #{age} ,
        </if>
    </set>
    WHERE id = #{id}
</update>

6.4 trim忽略前后缀

模拟where多条件查询。

<select id="selectStu" resultType="com.qf.pojo.Stu">
        SELECT id, name, age
        FROM stu
        <trim prefix="where" prefixOverrides="and | or">
            <if test="id != null">
                id = #{id}
            </if>

            <if test="name != null">
                name = #{name}
            </if>

            <if test="age != null">
            	age = #{age}
            </if>
        </trim>
</select>

6.5 foreach循环

<delete id="findAllStuById">
		delete from stu
    	where stuId in
    	<!-- in关键字关联多个条件-->
    	<!-- collection储存查询、删除等条件的容器,类型有list、array、map--> 
		<!--写为调用该SQL的方法的形参中list、array、map等类型的参数-->
    	<!-- item对应的数据库的列名 -->
    	<!-- index下标号,从0开始 -->
        <!-- open以什么开头 -->
    	<!-- separator以什么分割 -->
    	<!-- close以什么结尾 -->
		<foreach collection="list" 
                 open="(" separator="," close=")" 
                 item="carId" index="i">
            <!--数据库列名-->
			#{carId}
		</foreach>
</delete>
<!--对比一下原生的删除多个的写法:DELETE FROM stu WHERE stuId in (2, 4, 5)-->

七、MyBatis工具类

7.1 封装工具类

  • Resource:用于获得读取配置文件的IO对象,耗费资源,建议通过IO一次性读取所有所需要的数据。

  • SqlSessionFactory:SqlSession工厂类,内存占用多,耗费资源,建议每个应用只创建一个对象。

  • SqlSession:相当于Connection,可控制事务,应为线程私有,不被多线程共享。

  • 将获得连接、关闭连接、提交事务、回滚事务、获得接口实现类等方法进行封装。

package com.qf.mybatis.utils;

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 java.io.InputStream;

public class MyBatisUtils {

  	//获得SqlSession工厂
    private static SqlSessionFactory factory;

  	//创建ThreadLocal绑定当前线程中的SqlSession对象
    private static final ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();

    static {
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            factory = new SqlSessionFactoryBuilder().build(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获得连接(从tl中获得当前线程SqlSession)
    private static SqlSession openSession(){
        SqlSession session = tl.get();
        if(session == null){
            session = factory.openSession();
            tl.set(session);
        }
        return session;
    }

    //释放连接(释放当前线程中的SqlSession)
    private static void closeSession(){
        SqlSession session = tl.get();
        session.close();
        tl.remove();
    }

    //提交事务(提交当前线程中的SqlSession所管理的事务)
    public static void commit(){
        SqlSession session = openSession();
        session.commit();
        closeSession();
    }

    //回滚事务(回滚当前线程中的SqlSession所管理的事务)
    public static void rollback(){
        SqlSession session = openSession();
        session.rollback();
        closeSession();
    }

    //获得接口实现类对象
    public static <T extends Object> T getMapper(Class<T> clazz){
        SqlSession session = openSession();
        return session.getMapper(clazz);
    }
}

7.2 测试工具类

调用MyBatisUtils中的封装方法。

@Test
public void testUtils() {
    try {
				UserDao userDao = MyBatisUtils.getMapper(UserDao.class);
				userDao.deleteUser(15);
				MyBatisUtils.commit();
		} catch (Exception e) {
				MyBatisUtils.rollback();
				e.printStackTrace();
		}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值