三.MyBatis

配置依赖

  1. 添加MyBatis的依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.0</version>
    </dependency>
  2. 添加MyBatis与Spring整合依赖:

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.0</version>
    </dependency>
  3. 添加JDBC相关的Spring

    版本必须与当前项目使用的spring-webmvc的版本完全相同。

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.3.9.RELEASE</version>
    </dependency>
  4. 添加MySQL驱动和数据库连接池的依赖:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.9</version>
    </dependency>
    <dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.4</version>
    </dependency>
  5. Junit内注解测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.3.9.RELEASE</version>
    <scope>test</scope>
    </dependency>
    1
    2
    3
    @RunWith(SpringRunner.class)
    @ContextConfiguration(locations={"classpath:spring.xml"})
    @ContextConfiguration(locations= {"classpath*:spring.xml","classpath*:spring_redis.xml"})

数据库流程

1. 配置并读取数据库文件[之前写过]

  • 配置数据库文件

    db.properties

    1
    2
    3
    4
    5
    6
    url=jdbc:mysql://localhost:3306/tedu_umsuseUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    driver=com.mysql.jdbc.Driver
    username=root
    password=root
    initialSize=2
    maxActive=10
  • spring.xml

    1
    <util:properties id="dbConfig" location="classpath:db.properties" />

2.连接数据库

  • spring.xml

    • 直接在BasicDataSource中查找属性;

    • 建立BasicDataSource与数据库配置文件之间的联系;

    • name:BasicDataSource;value:数据库配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="url" value="#{dbConfig.url}" />
    <property name="driverClassName" value="#{dbConfig.driver}"></property>
    <property name="username" value="#{dbConfig.username}" />
    <property name="password" value="#{dbConfig.password}" />
    <property name="initialSize" value="#{dbConfig.initialSize}" />
    <property name="maxActive" value="#{dbConfig.maxActive}" />
    </bean>

*测试连接数据库

1
2
3
4
5
6
7
8
@Test
public void getConnection() throws SQLException {
AbstractApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
BasicDataSource dataSource = ac.getBean("dataSource", BasicDataSource.class);
Connection conn = dataSource.getConnection();
System.out.println(conn);
ac.close();
}

3. 持久层接口

  1. 创建实体类

    用于被接口调用,并向其添加用于添加数据库的字段值

    1
    2
    3
    4
    public class User{
    private 数据类型 属性名;
    生成get/set/toString方法
    }
  2. 创建持久层接口

    调用User类,用于向数据库中添加记录

    增删改时返回值用 Insteger:用于返回修改的条目

    1
    2
    3
    public interface UserMapper(){
    Integer insert(User user);
    }
  3. 扫描持久层接口

    1
    2
    3
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="cn.tedu.mybatis" />
    </bean>

4. 映射Sql语句

  1. 下载并使用mapper.xml文件

    mapper.xml基础内容

    1
    2
    3
    <?xml version="1.0" encoding="UTF-8" ?>  
    <!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
    "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
  2. 配置执行命令

    mapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <mapper namespace="cn.tedu.mybatis.UserMapper">
    <insert id="持久接口方法名">//节点错误时依然生效,但是返回值出错
    INSERT INTO t_user (
    <!--属性顺序可以与mysal字段循序不一致,必须与musal字段名一致,可以与user属性不相同但是必须对应-->
    username, password,
    age, phone,
    email
    ) VALUES (
    <!--user属性名 -->
    #{username},#{password},
    #{age},#{phone},
    #{email}
    )
    </insert>
    </mapper>
  3. 调用并执行mapper.xml文件

    spring.xml

    直接在SqlSessionFactoryBean中配置

    1
    2
    3
    4
    5
    6
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 数据源:连接与数据源的关系:BasicDataSource-->
    <property name="dataSource" ref="dataSource" />
    <!-- XML位置 -->
    <property name="mapperLocations" value="classpath:mappers/*.xml" />
    </bean>

*测试添加条目

  • 注意事项:

    1. Spring能创建接口对象(通过代理对象的方法)
    2. 在Spring中可以通过set方法/自己书写/接收表单数据接收数据
    3. 默认spring只创建对象,但是不执行里面的方法
  • java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @TEST
    UserMapper userMapper = ac.getBean("userMapper", UserMapper.class);
    //也可以在Spring.xml文件中配置
    //User user = ac.getBean("user",User.class);
    User user = new User();
    user.setUsername("admin");
    user.setPassword("1234");
    user.setAge(18);
    user.setPhone("12345678901");
    user.setEmail("admin@tedu.com");
    //方法默认不执行,因此需要手动调用方法
    Integer rows = userMapper.insert(user);
    System.out.println("rows="+rows);

基础操作

1. 查询

​ @insert(sql语句)

  • 思路

    • 思路1:直接在xml上配置值;[不建议使用,不能复用]
    • 思路2:在街口抽象方法上加入参数:
      • 思路2.1:参数类型是String
      • 思路2.2:参数类型User[不生效]
    • 思路2:在接口抽象方法上加入参数(可以复用);
      • 思路2.1:返回值类型是String[推荐]
      • 思路2.2:返回值类型是User[User内的参数要包含查询语句参数]
  • 注意事项

    • resultType是接口类返回值类型的类地址(数组类型是单个类型的地址)
    • 返回值类型可以是任意的,resultType在写lang类型时可以省略
    • 除了添加数据以外其余的不能使用java实体类传入数据
  1. 查询整个列表

    1. 接口类

      1
      List<User> findAll()//配置一个抽象方法,返回值为列表
    2. mapper.xml

      1
      2
      3
      <select id="对应抽象方法" resultType="返回实体类地址">
      SELECT * FROM t_user
      </select>
    3. test

      1
      2
      3
      for(User user : list){
      system.out.println(user.toString());
      }
  2. 查询一条数据

    1. 接口类

      1
      User findOne(String username)//
    2. mapper.xml

      1
      2
      3
      4
      <select id="findOne">
      SELECT username FROM t_user WHERE username=#{username};
      <!--#{}内代表传入的值,接口值默认传入-->
      </select>
    3. test

      1
      2
      User user = userMapper.findByUsername("root");
      system.out.println(user);
  3. 查询单个字段值

2. 接口名称和字段名称不相同

​ 通常情况下不会有增加字段操作,因此个人不推荐这种做法

  1. 执行mysql命令增加新字段[只能在mysql中执行命令添加]

    1
    alter table t_user add column is_delete int;
  2. 实体类[额外操作]

    1
    2
    private Integer isDelese;
    增加get set方法,重新修改toString方法
  3. 接口类[额外操作]

    1
    Integer addAlter();//返回这类型Integer:是否成功;参数列表:不添加不用写
  4. mapper.xml[额外操作]

    修改之前用到字段名的标签,增加没有写的新字段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <insert id="持久接口方法名">//节点错误时依然生效,但是返回值出错
    INSERT INTO t_user (
    username, password,
    age, phone,
    email,is_delete<!--字段名称-->
    ) VALUES (
    #{username},#{password},
    #{age},#{phone},
    #{email},#{isDelete}<!--属性名称-->
    )
    </insert>
  • 注意事项:

    • 出现问题

      当出现了字段名与实体类中的属性名不一致时,查询时的字段值显示为null

    • 解决方法

      不能使用select * 了,因为不能添加别名,生产情况中*不常用

      1
      2
      3
      <select id="findAll" resultAll="cn.tedu.mybatis.User">
      select username, password, age, phone, email,is_delete as isDelete from t_user;
      </select>

3. 更新字段值

  1. 接口类

    1
    Integer updateIsDelete(Integer isDelete);
  1. mapper.xml

    1
    2
    3
    <update id="updateIsDelete">
    UPDATE t_user SET is_delete = #{isDelete}
    </update>
  2. Test

    1
    2
    3
    4
    @Test
    public void a(){
    Integer rows = 接口类对象.updateIsDelete(0);
    }

4.传入多个参数

  1. 常规思路[错误的演示]

    1. 接口类

      1
      Integer updateUandP(String username,String password);
    2. mapper.xml

      1
      2
      3
      <update id="updateUandP">
      UPDATE t_user SET password = #{password} WHERE USERNAME = #{username}
      </update>
    3. test

      1
      2
      3
      4
      @Test
      public void updateUandP(){
      Integer rows = 接口对象.updateUandP("root","123456");
      }
    4. 错误提示

      1
      2
      Caused by: org.apache.ibatis.binding.BindingException: Parameter 'password' not found. 
      Available parameters are [arg1, arg0, param1, param2]
    5. 原因
      MyBatis框架只识别一个参数:默认传入四个参数,三个是框架用的不可见,一个是用户使用的,

  1. 使用Map传入两个参数[不完善,不推荐]

    • 必须知道map传入了几个数值,传入的类型,局限性太大,太麻烦因此不推荐
    1. 接口类

      1
      Integer updateUandP(SMap map);
    2. mapper.xml

      参数名必须与map中键名相同

      1
      2
      3
      <update id="updateUandP">
      UPDATE t_user SET password = #{password1} WHERE USERNAME = #{username1}
      </update>
    3. test

      1
      2
      3
      4
      5
      6
      7
      @Test
      public updateUandp2(){
      Map map = new HashMap<String,Object>();
      map.put("password1","longda101");
      map.put("username1","root103");
      Integer rows = 接口对象.updateUandP2(map);
      }
  2. 注解

    1. 接口类

      1
      Integer updateUandP3(@param("username3") String password3,@Param("password3") String password3);
    2. mapper.xml

      参数名必须与map中键名相同

      1
      2
      3
      <update id="updateUadnp3">
      UPDATE t_user SET password = #{password3} WHERE USERNAME = #{username3}
      </update>
    3. test

      1
      2
      3
      4
      @Test
      public updateUandp3(){
      Integer rows = 接口对象.updateUandP3("root103","longdan103");
      }

5. 获取新增记录的自增长ID

  1. 接口类

    1
    Integer insertAutoId();
  1. mapper.xml

    useGeneratedKeys=”true”:启动获取新增记录自增长ID

    keyProperty=”参数名”:返回到实体类对象的一个参数值中,替换原来的值

    1
    2
    3
    <insert useGeneratedKeys="true" keyProperty="id">
    INSERT INTO t_user(username,password) VALUES(#{username},#{password})
    </insert>
  2. test

    两次ID的值不一样,第二次为返回的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    public void insertAutoId(){
    User user = new User();
    user.setUsername("rootx");
    user.setPassword("rootx");
    syso(user.getID());//此时user中的ID为X
    Insert rows = 接口对象.insertAutoID();
    syso(user.getID());//此时user中的ID为Y
    }

6.统计当前数据库中数据的数量

​ 类似于思路2.1的方法

  1. 接口

    1
    Integer countID();
  2. mapper.xml

    1
    2
    3
    <select id="countID" resultType="Integer">
    SELECT COUNT(id) FROM t_user;
    </select>
  3. test

    1
    2
    3
    4
    5
    @Test
    public void countID(){
    Integer countID = userMapper.countID();
    System.out.println(countID);
    }

7. 多表关联查询

​ 因为没有能与关联查询相匹配的实体类,因此我们在这里使用VO类

  1. 新建VO类

    1
    2
    3
    4
    5
    public void UserVO(){
    user类属性
    department类属性
    生成get/set/toString方法
    }
  2. 接口

    1
    List<UserVO> findAll2();
  3. mapper.xml

    1
    2
    3
    <select id="findAll2" resultType="cn.tedu.mybatis.UserVO">
    SELECT t_user.id, username,password, age,is_delete AS isDelete,department_id,name FROM t_user LEFT JOIN t_department ON t_user.department_id=t_department.id
    </select>
  4. test

    1
    2
    3
    4
    List<UserVO> list = userMapper.findAll2();
    for(UserVO userVO : list){
    System.out.println(userVO);
    }

动态SQL

​ 目前理解为参数为多个相同类型,不能直接传或者map或者@param

1. <foreach>标签

属性 作用 选项 状态
collection 传入类型 list/array/map/set 必选
item 别名,相当于id键 必选
index 迭代次数,值 可选
separator 为传入的数据之间添加分隔符 “,” 可选
open foreach开启之前的sql语句 “(“ 可选/[不推荐]
close foreach开始之后的sql语句 “)” 可选/[不推荐]
  1. sql语句

    1
    DELETE FROM t_user WHERE id IN (2,4,5);
  2. 接口

    因为是多个相同类型,这里建议用数组或者集合

    1
    Integer deleteByIDs(Integer[] ids);//数组
  3. mapper.xml

    • 内容

      1
      2
      3
      4
      5
      6
      <delete id="deleteByIDs">
      DELETE FROM t_user WHERE id IN
      <foreach collection="array" item="id" separator="," open="(" close=")">
      #{id}
      </foreach>
      </delete>
    • 详解

      1
      2
      3
      for(Integer item : (collection)ids){
      System.out.print(item);
      }
  4. test

    1
    2
    3
    4
    5
    6
    @Test
    public void DeleteByID(){
    Integer[] ids = {3,5,7};
    Integer rows = userMapper.deleteByIDs(ids);
    System.out.println(rows;
    }

2. <if>标签

  1. 抽象类

    1
    List<User> findIf(@pParam("where") String whereX);
  2. mapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    <select id="findIf">
    SELECT * FROM t_user
    <if test="where != null"//if
    WHERE ${where}
    </if>
    <if test="where == null">//else,在本题目中可以不写
    </if>
    </select>
  3. test

    在传入值是一个参数时用#{},当传入值是一段话时用${}

    JDBC中的?处理一个参数时不用加引号,因为是预编译,预编译不支持引号,即便是or语句也会识别成一个参数,因此在这里不能用#{},只能使用${}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Test
    public void findIf(){
    syso("where != null");
    String whereX = "username = 'root'";
    List<User> list1 = 接口类对象.findIf(whereX);
    for(User user : list1){
    syso(user);
    }
    syso("where == null");
    List<User> list2 = 接口类对象.findIf(whereX);
    for(User user: list2){
    syso(user);
    }
    }

resultMap

​ 将查询到的结果封装到对象中

  1. 错误的演示[错误]

    1. 新建VO类

      1
      2
      3
      private Integer id;
      private String name;
      private List<User> user;//此处引用了一个对象,而不是对象内的属性
    2. 接口

      1
      DepartmentVO findById(Integer id);
    3. mapper.xml

      1
      2
      3
      <select id="findById" resultType="cn.tedu.myBatis.DepartmentVO">
      SELECT t_department.id AS did,t_department.name,t_user.id AS uid,username, password,age, phone,email, is_delete,department_id FROM t_department LEFT JOIN t_user ON t_department.id=t_user.department_id WHERE t_department.id=#{id};
      </select>
    4. 错误提示

      因为在VO类中引用了User对象,而在返回时查询到的数据不能封装到User对象中,因而报错

      1
      Caused by: org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
  2. resultMap节点

    需要封装到对象中的数据写在collection子节点中,不需要的直接写在resultMap节点中

    节点 作用 注意
    id id名 resultMap属性
    type 查询返回值类型地址 resultMap属性
    collection 用于配置1:N关系的数据(需要封装到对象中的数据) resultMap子节点
    ofType 集合中的元素的类型地址 collection属性
    id 用于配置主键字段的值的封装 collection子节点/resultMap子节点
    result 用于配置非主键字段的封装 collection子节点/resultMap子节点
    column mysql查询的字段名(有别名时为别名) id/result属性
    property 要封装的属性名 id/result属性
  3. 注意事项

    • 此处属性值和字段名不相同时不需要写别名,只有在单纯查询时需要写,这里的resultMap已经写了关系,所以不用写
    • select的resultType节点改为resultMap节点
    • 可以只使用result,而不使用id,但是推荐使用,因为mybatis会基于id的配置查询结果的缓存
    • 当有两个相同的属性值时(正常属性值和导入属性值相同时)需要修改别名
    • 添加<catch/>可以进行缓存,增删改删除缓存,缓存方便查找,因此经常增删改不应该加缓存
  4. 正确的方法

    1. 节点

      1
      DepartmentVO findById(Integer id);
    2. mapper.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <select id="findById" resultMap="DepartmentVO_Map">//使用新节点
      SELECT t_department.id AS did,t_department.name,t_user.id AS uid,username, password,age, phone,email, is_delete,department_id FROM t_department LEFT JOIN t_user ON t_department.id=t_user.department_id WHERE t_department.id=#{id};
      </select>

      <resultMap id="DepartmentVO_Map" type="cn.tedu.mybatis.DepartmentVO" >
      <id column="did" property="id" />
      <result column="name" property="name" />
      <collection property="需要封装到的对象名" ofType="cn.tedu.mybatis.User">
      <id column="uid" property="id" />
      <result column="username" property="username" />
      <result column="password" property="password" />
      <result column="age" property="age" />
      <result column="phone" property="phone" />
      <result column="email" property="email" />
      <result column="is_delete" property="isDelete" />
      <result column="department_id" property="departmentId" />
      </collection>
      </resultMap>
    3. test

      1
      2
      DepartmentVO departmentVO = departmentMapper.findById(3);
      System.out.println(departmentVO);

最后更新: 2019年11月06日 11:36

原始链接: https://airbash.cn/2019/11/02/Spring/MyBatis/

× 请我吃糖~
打赏二维码