Mybatis

Author Avatar
kevin
发表:2020-12-11 00:22:00
修改:2024-10-13 20:37:00

Mybatis常用配置

mysql,3306多个参数使用,&分隔

中文需要添加characterEncoding=UTF8"

参数只有一个时,名字随意

使用like模糊查询时,%不能写在映射文件,只能写在参数里

#{随意} 占位符,常用,运行期间编译成?问号,防止sql注入

${},传参时必须加入单引号,一般不用,直接拼接,没有防注入

适合用列名使用

参数是引用数据类型,参数名是javabean对象的属性名

执行增删改,自动开启事务,需要手动提交事务!!!!!!!!!!!

全局配置

<?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>
<!--    设置log4j对象,交给spring容器-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
<!--    支持多环境开发,默认是id为开发环境-->
    <environments default="development">
<!--        id为development的开发环境-->
        <environment id="development">
<!--            配置事务管理器-->
            <transactionManager type="JDBC"/>
<!--            配置连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                //加上编码,不然容易乱码,多个参数使用转义分隔,&amp;
                <property name="url" value="jdbc:mysql://localhost:3306/book_test?characterEncoding=UTF8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--
                配置一个xml文件,存储的SQL语句
                resource 配置的是存储SQL语句的文件路径和文件名
                一个表一个文件
        -->
        <mapper resource="User.xml"/>
    </mappers>
</configuration>

映射sql文件

<?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.stu.mybatis.User">
<!--    select标签,配置查询语句标签,语义化标签-->
<!--
当前文件的id查询语句的唯一性
resultType是返回类型
-->
    <select id="listUser" resultType="com.stu.User">
        <!-- select 标签体文本,就是SQL语句-->
        select user_id userId,user_name userName,password,email from t_user
    </select>
<!--
增加用户,parameter表示参数类型
多个参数,如果是引用类型,字段必须是引用的字段名
-->
    <insert id="insertUser" parameterType="com.stu.User">
        insert into  t_user values (#{userId},#{userName},#{password},#{email});
    </insert>
<!--
删除用户
参数类型是int,传入id删除某个用户
-->
    <delete id="deleteUser" parameterType="int">
        delete from t_user where user_id = #{id};
    </delete>
<!--    修改用户,多个参数,如果是引用类型,字段必须是引用的字段名-->
    <update id="updateUser" parameterType="com.stu.User">
        update  t_user set user_name = #{userName} ,password = #{password},email = #{email} where user_id = #{userId};
    </update>
<!--    查询单个用户,返回类型是user类-->
    <select id="selectOneUser" parameterType="int" resultType="com.stu.User">
         select user_id userId,user_name userName,password,email from t_user where user_id = #{id}
    </select>
<!--    插入后返回主键,有自增长,key的属性必须是类字段-->
    <insert id="insertGetKey" keyProperty="userId" parameterType="com.stu.User" useGeneratedKeys="true">
        insert into  t_user values (#{userId},#{userName},#{password},#{email});
    </insert>
<!--    插入后返回主键,没有自增长
       配置子参数,使用select_last_id获得主键,执行顺序在sql语句之后
       要存储的属性值,返回值类型
-->
    <insert id="insertGetNoKey" parameterType="com.stu.User">

        <selectKey order="AFTER" keyProperty="userId" resultType="int">
            select last_insert_id()
        </selectKey>
        insert into  t_user values (#{userId},#{userName},#{password},#{email});
    </insert>
<!--    模糊查询-->
    <select id="selectByLike" parameterType="String" resultType="com.stu.User">
        select user_id userId,user_name userName,password,email from t_user where user_name like #{name}
    </select>
</mapper>
配置新增的SQL语句,参数是EmployeeVo对象
       Vo对象中,包装Employee对象   Vo  Value-Object
<insert id="insertEmployeeVo" parameterType="com.atguigu.pojo.EmployeeVo">
<!--  参数是一个对象的包装类型, 包装类型的字段名.就行 -->
insert into employee values(#{employee.id},#{employee.name},#{employee.salary})
</insert>

resultType是返回类型不能和resultMap同时使用

使用步骤

 //读取配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//        得到sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//        得到session实例,执行已经映射的sql实例
        SqlSession session=sqlSessionFactory.openSession();
        //传入一个唯一标识
        List<User> userList = session.selectList("com.stu.mybatis.User.listUser");

函数接口--动态代理很重要

包名舍去dao,daoImpl,统一叫mapper包

四合一

- sql映射文件的namespace必须和mapper接口的全限定类名保持一致
- mapper接口的接口方法名必须和xml中的sql语句id保持一致
- mapper接口的接口方法形参类型必须和sql语句的输入参数类型保持一致
- mapper接口的接口方法返回类型必须和sql语句的resultType保持一致
<!--   和函数式接口绑定 ,使用接口全名   -->
<mapper namespace="com.stu.mapper.UserMapper">
</mapper>

使用类的别名-不区分大小写

    <typeAliases>
        <!-- 为pojo对象定义别名,不区分大小写-->
<!--        <typeAlias type="com.stu.User" alias="user"></typeAlias>-->
<!--        使用包扫描,扫描stu包下的所有类-->
        <!--
        typeAliases里面就是别名配置
            1. 每一个typeAlias标签就表示配置一个别名
                type属性:要进行别名配置的类
                alias属性:取的别名
            2. 因为所有的POJO类基本上都是放在同一个包中,所以我们可以采用包扫描进行别名配置
               用package标签进行包扫描,别名就是该类的类名(不区分大小写)
		我们一般采用第二种(也就是包扫描的方式进行别名配置)
    -->
        <package name="com.stu"/>
    </typeAliases>

使用框架的包扫描,子标签package,名字自动重命名,就是类名

<!-- 使用别名即可-->
<!--    查询单个用户,返回类型是user类,改为接口方法-->
    <select id="queryUserById" parameterType="int" resultType="user">
         select user_id userId,user_name userName,password,email from t_user where user_id = #{id}
    </select>

全局配置sql配置文件

多个sql配置文件,一个个去映射会显得很臃肿,所以把文件放到同名mapper路径下

在resource里配置同名包路径com/stu/mapper把sql配置文件放在这里

注意,创建文件夹不能直接写(.) 写(/)

配置文件名字和接口名字一致

创建和包名路径一致的文件
com/stu/mapper      创建文件时,会自动变成点
UserMapper.xml放在这
    <mappers>
        <!--
                配置一个xml文件,存储的SQL语句
                resource 配置的是存储SQL语句的文件路径和文件名
                一个表一个文件
        -->
<!--        <mapper resource="UserTwo.xml"/>-->
        //统一使用包扫描
        
    <package name="com.stu.mapper"/>
    </mappers>

增删改可以定义接口方法的时候定义为boolean

int返回受影响的行数,boolean返回大于0行就是true

获得sqlSession的时候,可以填入true,opensession(true),可以自动提交事务

配置使自动映射驼峰命名规则

<settings>
        <!--    设置log4j对象,交给spring容器-->
        <setting name="logImpl" value="LOG4J"/>
        <!-- 具体配置 -->
        <!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 -->
        <!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 -->
        <!-- 规则要求数据库表字段命名方式:单词_单词 -->
        <!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

手动映射

当名字不一样,且也不符合自动映射的规则时

使用手动映射

  • resultMap标签可以实现手动映射
    • id属性:自定义的唯一值
    • type属性:手动映射后的查询结果集类型
    • id子标签:映射主键使用
      • property属性:对应pojo对象的字段名
      • column属性:对应数据表的列名
    • result子标签:映射数据表其他字段
      • property属性:对应pojo对象的字段名
      • column属性:对应数据表的列名
  • select标签属性resultMap,关联resultMap标签的id值
  • id可以取名为select的id加resultMap,见名之意
  • resultType是返回类型不能和resultMap同时使用---------------------
<!--
        手动映射:通过resultMap标签配置映射规则
            1. id属性:表示这个手动映射规则的唯一表示
            2. type属性: 表示这个手动映射规则是将结果集映射给哪个类的对象,就是JavaBean类的全限定名
        resultMap标签中的子标签就是一一指定映射规则:
            1. id标签:指定主键的映射规则
            2. result标签:指定非主键的映射规则
        id标签和result标签的属性:
            1. column:要进行映射的结果集的字段名
            2. property:要进行映射的JavaBean的属性名
    -->

实现步骤

<resultMap id="EmployeeInfoMap" type="com.atguigu.pojo.EmployeeInfo">
    <id column="emp_id" property="id"/>
    <result column="emp_name" property="name"/>
    <result column="emp_salary" property="salary"/>
</resultMap>


<!--
        在select标签中通过resultMap属性来指定使用哪个手动映射规则
    -->
<select id="selectEmployeeInfoByEmpId" resultMap="EmployeeInfoMap">
    select * from t_emp where emp_id=#{empId}
</select>

返回自增长主键

<!--
插入后返回主键,有自增长,key的属性必须是类字段
 useGeneratedKeys属性字面意思就是“使用生成的主键”
 keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性
-->
    <insert id="insertGetKey" keyProperty="userId" parameterType="user" useGeneratedKeys="true">
        insert into  t_user values (#{userId},#{userName},#{password},#{email});
    </insert>

不支持自增主键的数据库怎么获取主键值

<!--    插入后返回主键,没有自增长
       配置子参数,使用select_last_id获得主键,执行顺序在sql语句之后
       要存储的属性值,返回值类型
       可以使用 selectKey 子元素:selectKey元素将会首先运行,id 会被设置,然后插入语句会被调用 
-->
    <insert id="insertGetNoKey" parameterType="user">

        <selectKey order="AFTER" keyProperty="userId" resultType="int">
            select last_insert_id()
        </selectKey>
        insert into  t_user values (#{userId},#{userName},#{password},#{email});
    </insert>

注解

sql注解写在接口方法上

适合简单业务,复杂业务还是得配置文件

//主键查询用户
@Select("select id,username,sex,birthday,address from user where id = #{id}")
User queryUserById(int id);
//查询全部用户
@Select("select id,username,sex,birthday,address from user")
List<User> queryUserByList();

假设sql查询结果没有对应的javabean,可以使用map封装

键是id,值是每一条封装好的对象

@mapKey("要作为key的属性值")
map<Integer User> getUser(String name);

@Insert注解

注解属性value:写入SQL语句

//新增用户数据
@Insert("insert into user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address})")
void saveUser(User user);

@Options注解

实现添加新数据的主键封装

注解属性

  • useGeneratedKeys:使用生成的主键,配置为true
  • keyProperty:主键封装的pojo对象属性

@SelectKey注解

  • 实现添加新数据的主键封装
  • 注解属性
    • statement:要执行的SQL语句
    • before:在添加SQL语句之前还是之后进行,配置为false
    • keyProperty:主键封装的pojo对象属性

@Update注解

注解属性value:写入SQL语句

//更新用户
@Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
void updateUser(User user);

@Delete注解

//删除用户
@Delete("delete from user where id = #{id}")
void deleteUser(int id);

假设包下面有个子包,子包也有个同名的类

可以使用注解取别名

@Alias("别名") 注明在类上

一对一的注解开发

@Results注解

配置手动映射,取代resultMap标签

OrdersMapper接口


//根据订单查询用户,一对一查询
@Select(" SELECT o.id,o.user_id,o.number,o.createtime,o.note FROM orders o")
@Results({
        //配置主键映射,id默认false,不是主键
        @Result(id = true,column = "id",property = "id"),
        //配置其他映射关系
        @Result(column = "user_id",property = "userId"),
        @Result(column = "number",property = "number"),
        @Result(column = "createtime",property = "createtime"),
        @Result(column = "note",property = "note"),
        /*
             配置关联查询用户表
             property查询的pojo对象哪个属性做为条件查询
               这个属性还是个pojo对象
             column查询条件的pojo对象的属性
             @One注解配置一对一的另一个查询语句
               此语句需要对应的接口方法出现
         */
        @Result(column = "user_id",property = "user",javaType = User.class,
                one = @One(select = "com.atguigu.mapper.UserMapper.queryUserByUserId",fetchType = FetchType.LAZY))
})
List<Orders> queryOrdersUser();

UserMapper接口

@Select("select id,username,sex,birthday,address from user where id=#{user_Id}")
User queryUserByUserId(Integer id);

一对多注解开发

UserMapper接口

//用户查询订单,一对多
@Select("select id,username,sex,birthday,address from user")
@Results({
        @Result(id = true,column = "id",property = "id"),
        @Result(column = "username",property ="username" ),
        @Result(column = "sex",property ="sex" ),
        @Result(column = "birthday",property ="birthday" ),
        @Result(column = "address",property ="address" ),
        @Result(column = "id",property = "ordersList",javaType = List.class,
        many = @Many(select = "com.atguigu.mapper.OrdersMapper.queryOrdersByUserId",fetchType = FetchType.LAZY))
})
List<User> queryUserOrders();

OrdersMapper接口

//用户查询订单,一对多
@Select("select number,createtime from orders where user_id = #{user_id}" )
List<Orders> queryOrdersByUserId(Integer user_id);

表查询-------常用

sql配置文件参数parameterType可以省略

1对1查询,一个订单对应一个用户

association(联合)标签,实现手动映射

  • propery属性:封装的pojo对象
  • javaType属性:封装的pojo对象类型
  • autoMapping属性,如果值对象也符合驼峰命名,则开启自动映射

即使全局配置了驼峰自动映射,但是属性 autoMapping="true"还是得开启才有数据

胡老师
    <!--    一对一查询,通过订单查询用户2-->
    <select id="queryOrdersUser2" resultMap="queryOrdersUserResultMap2" >
         select o.order_id,o.user_id,o.number,u.address,u.user_name from orders o left join user u
        on o.user_id=u.user_id
    </select>
    <!--    手动设置映射2-->
    <resultMap id="queryOrdersUserResultMap2" type="orders" autoMapping="true">

        <!-- 手动映射,配置User对象,property字段名,javaType封装类型-->
        <association  property="user" javaType="user" autoMapping="true">
            <!-- 手动映射,配置User对象,property字段名,javaType封装类型-->
        </association>
    </resultMap>


石松老师
使用association标签
<!--    一对一查询,通过订单查询用户-->
    <select id="queryOrdersUser" resultMap="queryOrdersUserResultMap" >
         select o.order_id,o.user_id,o.number,u.address,u.user_name from orders o left join user u
        on o.user_id=u.user_id
    </select>
<!--    手动设置映射-->
    <resultMap id="queryOrdersUserResultMap" type="orders">
        <id column="order_id" property="OrderId"></id>
        <result column="user_id" property="userId"></result>
        <result column="number" property="number"></result>
        <result column="createtime" property="createtime"></result>
        <!-- 手动映射,配置User对象,property字段名,javaType封装类型-->
        <association  property="user" javaType="user">
            <id column="user_id" property="userId"></id>
            <result column="address" property="address"></result>
            <result column="user_name" property="userName"></result>
        </association>
    </resultMap>


尚硅谷方式
<!--    手动设置映射-->
    <resultMap id="queryOrdersUserResultMap" type="orders">
        <id column="order_id" property="OrderId"></id>
        <result column="user_id" property="userId"></result>
        <result column="number" property="number"></result>
        <result column="createtime" property="createtime"></result>
        <result column="user_name" property="user.userName">
        </result>
        ...使用值属性的.可以调用值
    </resultMap>

一对多表查询

1对多查询,一个用户对应多个订单,不要使用自动映射

public interface UserMapper {

    //根据用户名和性别查询用户
     List<User> queryUserByWhere(User user);
     //订单一对一查询用户
    List<Orders> queryOrdersUser();
    //订单一对一查询用户2
    List<Orders> queryOrdersUser2();
    //查询用户的多个订单
    List<User> queryUserOrders();
    //根据用户id查询订单
    User queryOrdersById(Integer id);

}

xml配置文件

<!--    根据用户id查询订单-->
    <select id="queryOrdersById"  resultMap="queryOrdersByIdResultMap">
    select * from user u,orders o where
    u.user_id = o.user_id and u.user_id =  #{id}
    </select>
    <resultMap id="queryOrdersByIdResultMap" type="user" >
<!--        多个结果不能自动映射在一个pojo,需要手动映射,返回结果是一个用户,自定映射的多条数据装不了-->
        <id column="user_id" property="userId"></id>
<!--        多个订单映射结果有多个,而返回类型刚好是list,所以可以使用自动映射-->
    <collection property="ordersList"  ofType="orders" autoMapping="true"></collection>
    </resultMap>

注意

一对多当使用自动映射的时候,会报错,因为自动映射会返回三条数据,而用户只有一个

手动映射则不会报错

Expected one result (or null) to be returned by selectOne(), but found: 2

多对多查询

public class Role {
    private Integer roleId;
    private String roleName;
    private List<User> userList;
}

xml配置文件

<mapper namespace="com.stu.mapper.RoleMapper">
    <!--    多对多查询-->
    <select id="queryRoleUser" resultMap="queryRoleUserResultMap">
    select * from role r left join user_role ur on r.`role_id` = ur.`role_id`
    left join user u on u.`user_id` = ur.`user_id`
</select>
    <resultMap id="queryRoleUserResultMap" type="role">
        <id column="role_id" property="roleId"></id>
        <result column="role_name" property="roleName"></result>
        <collection property="userList" ofType="user" autoMapping="true"></collection>
    </resultMap>
</mapper>


输出结果

  List<Role> roles = mapper.queryRoleUser();
        if (roles.size() > 0 && roles != null){
            for (Role role : roles) {
                System.out.println(role);
            }
        }

延迟加载

延迟加载就是对于实体类关联的属性到需要使用时才查询

查询到user的时候,不一定会使用Order的List集合数据。如果Order的集合数据始终没有使用,那么这部分数据占用的内存就浪费了

为了实现延迟加载,对Customer和Order的查询必须分开,分成两步来做

局部延迟加载

        <association property="user" column="user_id" 
                    // 延迟加载
                     fetchType="lazy" select="com.stu.mapper.UserMapper.selectUserById"></association>

全局延迟加载

  <settings>
        <!-- 开启延迟加载功能 -->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

分步查询

一对一分步查询

接口方法

//传入一个订单查询用户分步式和懒加载
Order selectOrderUser(Integer id);

orderMapper

<!--    传入一个订单查询用户分步式和懒加载-->
    <select id="selectOrderUser" resultMap="selectOrderUserResultMap">
         select order_id,user_id,number,createtime from orders where order_id = #{id}
    </select>
    <resultMap id="selectOrderUserResultMap" type="Order" >
        <id column="order_id" property="OrderId"></id>
        <result column="user_id" property="userId"></result>
<!--        开始和用户表交互,开启懒加载-->
        <association property="user" column="user_id" fetchType="lazy" select="com.stu.mapper.UserMapper.selectUserById"></association>
    </resultMap>

userMapper

<!--    传入一个用户id查询用户信息-->
    <select id="selectUserById" resultMap="selectUserByIdResultMap">
        select user_id,user_name,sex,birthday,address from user where user_id = #{id}
    </select>
    <resultMap id="selectUserByIdResultMap" type="user">
        <id column="user_id" property="userId"></id>
        <result column="user_name" property="userName"></result>
    </resultMap>

一对多查询

//根据用户id查询订单,一对多
User selectOrderById(Integer id);

userMapper

<!--    根据用户id查询订单,一对多-->
    <select id="selectOrderById" resultMap="selectOrderByIdResultMap">
         select user_id,user_name,sex,birthday,address from user where user_id = #{id}
    </select>
    <resultMap id="selectOrderByIdResultMap" type="user">
        <id column="user_id"  property="userId"></id>
        <result column="user_name" property="userName"></result>
        <collection property="orderList" column="user_id" ofType="order" fetchType="lazy" select="com.stu.mapper.OrderMapper.selectOrderByUserId"></collection>
    </resultMap>

ordermapper

<!--    根据用户名id返回多个订单-->
    <select id="selectOrderByUserId"   resultMap="selectOrderByUserIdResultMap" >
    select  order_id,user_id,number,createtime from orders where user_id = #{id}
</select>
    <resultMap id="selectOrderByUserIdResultMap" type="Order" >
        <id column="order_id" property="OrderId"></id>
        <result column="user_id" property="userId"></result>
    </resultMap>
//根据用户名id返回多个订单
List<Order> selectOrderByUserId(Integer id);

mybatis不能使用in查询

自动遍历查询,使用=即可

评论