框架 - mybatis(动力节点:老杜从零学mybatis入门到架构思维)(二)

八、MyBatis小技巧

  1. 使用#{}和${}的区别

    • #{}:底层使用PreparedStatement。先进行SQL语句的编译,然后给SQL语句的占位符问号?传值。可以避免SQL注入的风险。

    • ${}:底层使用Statement。先进行SQL语句的拼接,然后再对SQL语句进行编译。存在SQL注入的风险。

    • 所以一般会使用#{},${}只有某些特定的场景才会使用。总结一下两者的根本区别就是#{}用于使用PreparedStatement给?传值,${}用于直接拼接sql语句。

  2. 有些地方可以使用${},体会下列例子

    • 场景:查询所有的账户列表,通过指定的排序模式,依据账户余额进行升序或者降序排序。

      • 编写dao接口:

        public interface AccountDao {
            // String descOrEsc表示升序或者降序排序
            List<Account> selectAll(String descOrEsc);
        }
        
      • 编写AccountMapper.xml文件:

        <mapper namespace="shh.dao.AccountDao">
            <select id="selectAll" resultType="shh.pojo.Account">
                select * from t_account order by balance #{descOrEsc};
            </select>
        </mapper>
        
      • 执行测试:执行报错

        public static void main(String[] args) {
            AccountDao accountDao = SqlSessionUtil.getSqlSession().getMapper(AccountDao.class);
            List<Account> desc = accountDao.selectAll("desc");
            System.out.println(desc);
        }
        

        分析:使用#{}之后,底层使用prepareStatement。首先得到sql:select * from t_account order by balance ?,进行拼接之后就是:select * from t_account order by balance 'desc',自动添加了单引号,所以执行报错。

      • 改用${}即可执行成功

        <mapper namespace="shh.dao.AccountDao">
            <select id="selectAll" resultType="shh.pojo.Account">
                select * from t_account order by balance ${descOrEsc};
            </select>
        </mapper>
        

        分析:拼接之后的sql是:select * from t_account order by balance desc,可以成功执行。

    • 需要在sql中拼接表名的时候:现实业务当中,可能会存在分表存储数据的情况。因为一张表存的话,数据量太大。查询效率比较低。可以将这些数据有规律的分表存储,这样在查询的时候效率就比较高。因为扫描的数据量变少了。

      例如日志表:专门存储日志信息的。如果t_log只有一张表,这张表中每一天都会产生很多log,慢慢的,这个表中数据会很多。怎么解决问题?可以每天生成一个新表。每张表以当天日期作为名称,例如:
      t_log_20220901、t_log_20220902

      • 你想知道某一天的日志信息怎么办?假设今天是20220901,那么直接查:t_log_20220901的表即可。那么查询某天的日志信息的时候,就需要找到某一天对应的表,然后将表名拼接到sql中

      • 这个时候如果使用#{},那么就会出现上一个例子中出现的异常。所以就需要使用${}来拼接sql

        <mapper namespace="shh.dao.AccountDao">
            <select id="selectAll" resultType="shh.pojo.Account">
                select * from ${tableName};
            </select>
        </mapper>
        
    • 一次性删除多条数据:删除多条数据,要么使用or,要么使用in操作,所以拼接sql的时候也可能会发生错误,所以会使用${}。

    • 模糊查询:like

      • 需求:根据汽车品牌进行模糊查询:select * from t_car where brand like '%奔驰%';

      • 第一种方案:'%${brand}%'(这里面就不能使用#{},因为编译之后是这样的:'%?%',prepareStatement无法给单引号中的?传值。)

      • 第二种方案:concat函数,这个是mysql数据库当中的一个函数,专门进行字符串拼接concat('%',#{brand},'%')

      • 第三种方案:"%"#{brand}"%"(使用#{},因为编译之后是这样的:%?%,?不在单引号里面了,prepareStatement可以给?传值。)

  3. 别名机制

    • 在XxxMapper.xml文件中,经常需要执行参数类型,返回值数据类型,这个数据类型一般会比较长,例如以下,就是:shh.dao.AccountDao,非常不方便,所以mybatis提供了别名机制。

      <mapper namespace="shh.dao.AccountDao">
          <select id="selectByNo" resultType="shh.pojo.Account">
              select * from t_account where actno = #{no};
          </select>
          <select id="selectAll" resultType="shh.pojo.Account">
              select * from t_account order by balance ${descOrEsc};
          </select>
      </mapper>
      
    • 为某个类执行别名:在mybatis-config.xml核心配置文件中,可以进行以下配置,为某个类起别名:

      <typeAliases>
          <!--type指的是要起别名的类名,alias表示要起的别名。起别名之后,使用别名时不区分大小写-->
          <typeAlias type="shh.pojo.Account" alias="account"></typeAlias>
      </typeAliases>
      
    • 可以使用mybatis的默认别名机制:

      <typeAliases>
          <!--使用mybatis的默认别名机制,默认的别名就是不带包名的类名,不区分大小写.-->
          <typeAlias type="shh.pojo.Account"></typeAlias>
      </typeAliases>
      
    • 还有更方便的起别名方式:执行某个包,该包下的所有类都会使用默认别名机制起别名。

      <typeAliases>
          <!--  这样一来,这个包下面的所有类都会使用默认别名.  -->
          <package name="shh.pojo"/>
      </typeAliases>
      
  4. mybatis-config.xml文件中的mappers标签。

    • mappers标签中的mapper标签有三种指定mapper路径的方式:

      <mapper resource="CarMapper.xml"/>    要求类的根路径下必须有:CarMapper.xml
      <mapper url="file:///d:/CarMapper.xml"/>    要求在d:/下有CarMapper.xml文件
      <mapper class="全限定接口名,带有包名"/>  
      
      • resource:这种方式是从类的根路径下开始查找资源。采用这种方式的话,你的配置文件需要放到类路径当中才行。

      • url:这种方式是一种绝对路径的方式,这种方式不要求配置文件必须放到类路径当中,哪里都行,只要提供一个绝对路径就行。这种方式使用极少,因为移植性太差。

      • class:这个位置提供的是mapper接口的全限定接口名,必须带有包名的。

    • 思考:mapper标签的作用是指定SqlMapper.xml文件的路径,使用class属性指定接口名有什么用呢?

      <mapper class="com.powernode .mybatis.mapper.CarMapper"/>
      如果你class指定是:com.powernode.mybatis.mapper.CarMapper
      那么mybatis框架会自动去com/powernode/mybatis/mapper目录下查找CarMapper.xml文件。
      注意:
      如果你采用这种方式,那么你必须保证CarMapper.xml文件和CarMapper接口必须在同一个目录下。
      
    • 但是在mappers中还有另一个可用标签:packet,这也是我们最常用的一种方式。需要注意的是如果使用这种方式,XML文件必须和接口放在一起。并且名字一致。否则会找不到配置文件而报错。

      <mappers>
          <!--前提是:XML文件必须和接口放在一起。并且名字一致。-->
          <package name="指定接口存放的包路径"/>
      </mappers>
      
  5. idea中可以添加文件模板。例如添加一个mybatis-config.xml的模板,下次需要mybatis-config.xml的时候,就可以直接使用模板创建出来了。(略过,要的时候再查)

  6. 获取自动生成的主键值

    • 场景:有一张表A,插入数据时会自动生成主键值id。有另一张表B,与表A关联,当在A中插入数据X的时候,需要在表B中存入相应的数据X的id值。那么就需要在在表A中插入数据X的同时获取到X的id值。

    • 编写Dao接口:

      public interface AccountDao {
          // account对象用于传递需要插入表中的数据参数,也用于接收生成的id值。
          public void insertAccount(Account account);
      }
      
    • 编写xxxMapper.xml文件

      <mapper namespace="shh.dao.AccountDao">
          <!-- useGeneratedKeys="true":表示使用自动生成的主键值。
          keyProperty="id":指定主键值赋值给对象的哪个属性。这个就表示将主键值赋值给Car对象的id属性。 
          -->
          <insert id="insertAccount" useGeneratedKeys="true" keyProperty="id">
              insert into t_account  values (null,#{actno},#{balance});
          </insert>
      </mapper>
      
    • 测试:

      public static void main(String[] args) {
          AccountDao accountDao = SqlSessionUtil.getSqlSession().getMapper(AccountDao.class);
          Account account = new Account(null, "aaa", 0);
          // account对象用于传递插入数据搜需要的参数,也用于接收生成的id值.
          accountDao.insertAccount( account);
          System.out.println(account);
      }
      

九、MyBatis参数处理(重要)

  1. mybatis中,比较核心的问题有两点,就是参数传递和结果返回。这一章就解决参数处理的问题,下一章解决返回值问题。

    • 参数处理问题:也就是研究dao接口中的参数传递到#{}中的问题

    • 结果返回:研究封装查询结果的问题。

  2. 单个简单类型参数

    • 简单类型是指:基本数据类型以及他们的包装类,String、java.util.Date、java.sql.Date

    • 只有单个参数的时候,Dao层接口中的方法中的参数名可以随便写,因为只有一个参数嘛,不会有任何歧义,Mybatis可以很简单地自动进行推断。

    • 然后在XxxxMapper.xml文件中,可以通过parameterType属性指定参数类型。这个属性的值可以省略,因为Mybatis可以自动推断参数类型,但是如果加上这个属性的话,性能会高一点,因为mybatis就不需要去解析参数类型了。

      <mapper namespace="shh.dao.AccountDao">
          <!-- 使用parameterType属性来指定参数类型 -->
          <select id="selectByName" resultType="student" parameter="java.lang.String">
              select * from t_student where name = #{name};
          </select>
      </mapper>
      
    • 注意parameterType属性也可以使用别名,也就是上面提到的核心配置文件中的typeAliases标签配置的别名,但是Mybatis中配置了一些内置的别名,可以参考Mybatis官方文档。

    • 注意还有另一种声明参数类型的方式:(了解即可,基本上不会用到)

      <select id="selectByName" resultType="student">
          select * from t_student where name = #{name,javaType=String,jdbcType=VARCHAR}
      </select>
      
  3. Map参数

    • 使用map来传递参数也是可以的,parameterType可以指定为map(内置别名),使用#{map中存放的key值} 来获取map中存放的数据。(在前面演示过了)**

    • 也可以不指定参数类型,只有一个参数的话,Mybatis可以自动推断参数类型。

  4. 使用实体类作为参数

    • 使用实体类来传递参数也是可以的,parameterType可以指定为全类名(也可以使用别名),使用#{实体类中的属性名} 来获取map中存放的数据。会自动调用实体类的get方法去获取数据。(在前面演示过了)

    • 也可以不指定参数类型,只有一个参数的话,Mybatis可以自动推断参数类型。

  5. 多个参数

    • 当有多个参数的时候,mybatis框架底层是怎么做的呢?mybatis框架会自动创建一个Map集合。并且Map集合是以这种方式存储参数的:例如接口方法中参数列表为(String name, char sex)

      map.put("arg0", name);
      map.put("arg1",sex);
      map.put("param1", name);
      map.put("param2",sex);

    • 示例:根据name和sex查询Student信息。

      • 编写dao接口

        public interface StudentDao {  
            // 有两个参数 age 和 sex
            Student queryByAgeAndSex(int age,Character sex);
        }
        
      • 如果XxxxMapper.xml使用以下配置:会执行报错,因为找不到age和sex参数

        <select id="queryByAgeAndSex" resultType="shh.pojo.Student">
            select * from student where age = #{age} and gender = #{sex}
        </select>
        
      • 必须使用mybatis底层定义的map集合存放的键值argX或者paramX,可以混合使用。例如:

        <select id="queryByAgeAndSex" resultType="shh.pojo.Student">
            select * from student where age = #{arg0} and gender = #{param2}
        </select>
        
  6. @Param注解

    • 在上面的多参数场景中,传递参数时必须使用内部定义的Map集合来传递参数,而定义的key值是argX,paramX,这就很不方便,缺乏可读性

    • 可以使用@Param("自定义参数名")注解,为Dao接口中的参数指定存放到Map集合中的键值,会替换掉argX这一部分键值,paramX这部分是还在的。

    • 使用示例

      • 在定义dao接口时:

        public interface StudentDao {
            Student queryByAgeAndSex(@Param("age") int age, @Param("sex") Character sex);
        }
        
      • 在XxxMapper中使用参数键值:

        <select id="queryByAgeAndSex" resultType="shh.pojo.Student">
            <!--  必须使用注解中的键值或者ParamX这部分键值 -->
            select * from students where age = #{age} and gender = #{sex}
        </select>
        
  7. @Param注解的源码分析:略

十、MyBatis查询结果专题(重要)

  1. 返回单个实体类

    • 其实就没啥特殊的,在XxxxMapper.xml中使用ResultType指定返回值类型即可(可以使用别名机制)。

    • 要注意的是,实体类中的属性属性名必须和表中的属性名一致,否则就会封装不进去实体类中。如果发现实体类和表中属性不一致,可以通过在sql中,对查询到的数据属性起别名来解决。(前面都讲过了,不再重复)

  2. 返回多个查询结果:返回List集合。

    • 当需要查询所有,也就是查询得到一个结果集LIst时,需要注意的是,在XxxxMapper.xml中ResultType的值是指定List中保存的数据的类型。

    • 例如:查询所有的学生信息

      • 编写Dao接口:

        public interface StudentDao {
            List<Student> queryAll();
        }
        
      • 编写XxxxMapper.xml:

        <mapper namespace="shh.dao.StudentDao">
            <!-- 注意resultType指定的是List集合保存的数据类型 -->
            <select id="queryAll" resultType="shh.pojo.Student">
                select * from students;
            </select>
        </mapper>
        
    • 注意:如果查询结果是多条数据的,但是没有使用List集合来接收数据,那么就会报错。

  3. 返回Map

    • 当返回的数据,没有合适的实体类对应的话,可以采用Map集合接收。字段名做key,字段值做value。查询如果可以保证只有一条数据,则返回一个Map集合即可。

    • 返回一个Map示例:

      • 编写Dao接口:

        public interface StudentDao {
            Map<String,Object> queryById(@Param("id") int id);
        }
        
      • 编写XxxMapper.xml文件

        <mapper namespace="shh.dao.StudentDao">
            <select id="queryById" resultType="java.util.Map">
                select * from students where id = #{id};
            </select>
        </mapper>
        
    • 返回多个Map示例:其实就是返回一个存储Map的List集合。

      • 编写Dao接口:

        public interface StudentDao {
            List<Map<String,Object>> queryAll();
        }
        
      • 编写XxxMapper.xml文件

        <mapper namespace="shh.dao.StudentDao">
            <select id="queryAll" resultType="java.util.Map">
                select * from students;
            </select>
        </mapper>
        
    • 在上面的例子中,在查找的结果List<Map<String,Object>>中,如果要获取ID=3的某一个对象的信息,那么就需要遍历List集合,获取Map集合,然后判断当前Map集合中保存的对象是否符合ID=3的条件,很不方便,可以采取另一种方式来保存多个Map:Map<Long,Map<String,Object>> resultMap,保存的数据类似下图,这样去除满足ID=3的某个对象的数据的时候,就可以直接通过Map集合的key值来判断了。

      • 编写Dao接口:需要使用MapKey来指定Map中,key值要保存的属性名。

        public interface StudentDao {
            // 需要使用MapKey来指定Map中,key值要保存的属性名。
            @MapKey("id")
            Map<Long,Map<String,Object>> queryAll();
        }
        
      • XxxMapper.xml文件:注意resultType的值。

        <mapper namespace="shh.dao.StudentDao">
            <select id="queryAll" resultType="java.util.Map">
                select * from students;
            </select>
        </mapper>
        
  4. ResultMap结果映射

    • 查询结果的列名和java对象的属性名对应不上怎么办?

      • as给列起别名

      • 使用resultMap进行结果映射:指定java类中的属性名和表中的属性名的对应关系。

      • 开启驼峰命名自动映射(配置settings)

    • 使用示例:

      • 实体类:

        public class Student {
            private Long id;
            private String n;
            private Integer a;
            private Character g;
            private Date b;
        
        }
        
      • 表:

      • dao接口:查询所有的Student数据

        public interface StudentDao {
            public List<Student> queryAll();
        }
        
      • 分析:可以看到Sutdent类中的属性名和表中的属性名完全不一致。如果依然使用以上展示的方式来实现的话,必然会由于属性名对应不上而报异常。这个时候就需要使用ResultMap映射来解决这个问题了(也可以使用起别名,但是开启驼峰匹配也没有意义了)。

      • XML文件:使用ResultMap来执行实体类和数据库表中的属性映射关系。

        <mapper namespace="shh.dao.StudentDao">
            <!--
            1.专门定义一个结果映射,在这个结果映射当中指定数据库表的字段名和Java类的属性名的对应关系。
            2.type属性:用来指定POJO类的类名。
            3. id属性:指定resultMap的唯一标识。这个id将来要在select标签中使用。
            -->
            <resultMap id="student" type="shh.pojo.Student">
                <!--如果这个表中有主键的话,建议使用id标签来指定主键的映射关系,可以提高执行效率。-->
                <id column="id" property="id"></id>
        
                <!--property后面填写POJO类的属性名-->
                <!--column后面填写数据库表的字段名-->
                <result column="name" property="n"></result>
                <result column="age" property="a"></result>
                <result column="gender" property="g"></result>
                <result column="birthday" property="b"></result>
            </resultMap>
            <!--使用resultMap属性来指定使用哪一个属性名映射,将属性值设置为已经定义了的某个resultMap的id值-->
            <select id="queryAll" resultMap="student">
                select * from students;
            </select>
        </mapper>
        
  5. 是否开启驼峰命名自动映射

    • 使用这种方式的前提是:属性名遵循Java的命名规范(小驼峰),数据库表的列名遵循SQL的命名规范。

      • Java命名规范:首字母小写,后面每个单词首字母大写,遵循驼峰命名方式。

      • SQL命名规范:全部小写,单词之间采用下划线分割。

      • 比如以下的对应关系:

    • 在mybatis-config.xml也就是mybatis的核心配置文件的settings标签中配置:

      <!--放在properties标签后面-->
      <settings>
          <setting name="mapUnderscoreToCamelCase"value="true"/>
      </settings>
      
  6. 返回总记录条数:也就是返回一个基本数据类型:没啥区别

十一、动态SQL(重要)

  1. 有些业务场景需要进行sql拼接,这就需要动态SQL。例如批量删除、多条件查询等等。

  2. if标签

    • 使用if标签的时候,需要注意

      • if标签中test属性是必须的。

      • if标签中test属性的值是false或者true。

      • 如果test是true,则if标签中的sql语句就会拼接。反之,则不会拼接。

      • 在mybatis的动态SQL当中,不能使用&&,只能使用and。

    • test属性中可以使用的是

      • 当使用了@Param注解,那么test中要出现的是@Param注解指定的参数名。@Param("brand"),那么这里只能使用brand

      • 当没有使用@Param注解,那么test中要出现的是param1 param2 param3 arg0 arg1 arg2....

      • 当使用了POJO,那么test中出现的是POJO类的属性名。

    • 场景示例:多条件查询,可能的条件包括品牌、指导价格、汽车类型。

      • 定义dao接口:

        public interface CarDao {
            List<Car> select(@Param("brand") String brand, @Param("guidePrice") Integer guidePrice, @Param("carType") String carType);
        }
        
      • 编写XxxMapper.xml文件

        <mapper namespace="shh.dao.CarDao">
            <select id="select" resultType="shh.pojo.Car">
                select * from t_car where 1 = 1
                    <if test="brand != null and brand != ''">
                        and brand like "%"#{brand}"%"
                    </if>
                    <if test="guidePrice != null and guidePrice !=''">
                        and #{guidePrice} > guide_price
                    </if>
                    <if test="carType != null and carType !=''">
                        and car_type = #{carType}
                    </if>
            </select>
        </mapper>
        
      • 注意:添加了where 1=1标签,是为了满足sql格式,否则会产生sql语法错误。

  3. where标签

    • where标签的作用:让where子句更加动态智能。所有条件都为空时,where标签保证不会生成where子句,并且可以自动去除某些条件前面多余的and或or(后面的and 或者 or不能去掉)。

    • 使用示例:

      • dao接口:沿用if例子中的dao接口即可

      • XML文件:

        <mapper namespace="shh.dao.CarDao">
            <select id="select" resultType="shh.pojo.Car">
                select * from t_car
                <where>
                    <if test="brand != null and brand != ''">
                        and brand like "%"#{brand}"%"
                    </if>
                    <if test="guidePrice != null and guidePrice !=''">
                        and #{guidePrice} > guide_price
                    </if>
                    <if test="carType != null and carType !=''">
                        and car_type = #{carType}
                    </if>
                </where>
            </select>
        </mapper>
        
      • 添加where标签,可以省略掉where 1=1 的条件判断,代码更加优雅了。

  4. trim标签:用于添加、删除前后缀

    • trim标签的属性:

      • prefix:加前缀

      • suffix:加后缀

      • prefix0verrides:删除前缀

      • suffixOverrides:删除后缀

    • 使用示例:

      • dao层接口:沿用上面的dao接口

      • Mapper.xml文件:

        <mapper namespace="shh.dao.CarDao">
            <select id="select" resultType="shh.pojo.Car">
                select * from t_car
                <!-- prefix="where"是在trim标签中所有内容的前面添加where,只有在trim标签中的内容生效了,才会添加where-->
                <!--suffix0verrides="and|or"把trim标签中内容的多余后缀and或or去掉-->
                <trim prefix="where" suffixOverrides="and|or">
                    <if test="brand != null and brand != ''">
                        brand like "%"#{brand}"%" and
                    </if>
                    <if test="guidePrice != null and guidePrice !=''">
                        #{guidePrice} > guide_price  and
                    </if>
                    <if test="carType != null and carType !=''">
                        car_type = #{carType}
                    </if>
                </trim>
            </select>
        </mapper>
        
  5. set标签

    • set标签:主要使用在update语句当中,用来生成set关键字,同时去掉最后多余的“,”,比如我们只更新提交的不为空的字段,如果提交的数据是空或者"",那么这个字段我们将不更新。

    • 示例:

      • dao接口

        public interface CarDao {
            int update(Car car);
        }
        
      • XML文件

        <mapper namespace="shh.dao.CarDao">
            <update id="update" parameterType="car">
                update t_car
                <set>
                    <if test="carNum != null and carNum != ''">car_num = #{carNum},</if>
                    <if test="brand != null and brand != ''">brand = #{brand},</if>
                    <if test="guidePrice != null and guidePrice != ''">guide_price = #{guidePrice},</if>
                    <if test="produceTime != null and produceTime != ''">produce_time = #{produceTime},</if>
                    <if test="carType != null and carType != ''">car_type = #{carType}</if>
                </set>
                where
                id = #{id};
            </update>
        </mapper>
        
  6. choose、when、otherwise

    • 这三个标签一般是一起使用的:类似if..else if...else结构。这里面,只能生效一个when或者otherwise。

      <choose>
          <when></when>
          <when></when>
          <when></when>
          <otherwise></otherwise>
      </choose>
      
    • 示例:先根据品牌查询,如果没有提供品牌,再根据指导价格查询,如果没有提供指导价格,就根据生产日期查询。

      • dao接口

        public interface CarDao {
            List<Car> select(@Param("brand") String brand, @Param("guidePrice") Integer guidePrice, @Param("carType") String carType);
        }
        
      • XML文件

        <select id="select" resultType="shh.pojo.Car">
            select * from t_car
            <where>
                <choose>
                    <when test="brand != null and brand != ''">
                        brand like "%"#{brand}"%" and
                    </when>
                     <when test="guidePrice != null and guidePrice !=''">
                         #{guidePrice} > guide_price  and
                     </when>
                     <otherwise>
                         car_type = #{carType}
                     </otherwise>
                </choose>
            </where>
        </select>
        
  7. foreach标签

    • 作用:循环数组或集合,动态生成sql。

    • foreach标签的属性:

      • collection:指定数组或者集合。这个数组或者集合的参数名依然需要满足前面提到的,存储到内置map集合中的key值的规则。

      • item:代表数组或集合中的元素

      • separator:循环之间的分隔符

      • open:for循环外面以什么开始

      • close:for循环外面以什么结束。

    • 示例:批量删除数据。

      • dao接口

        public interface CarDao {
            int delete(@Param("ids") long ids[]);
        }
        
      • XML文件

        <delete id="delete" >
            delete from t_car where id in (
                <foreach collection="ids" item="id" separator=",">
                    #{id}
                </foreach>
            )
        </delete>
        
    • open和close属性示例:下方写法与上方写法效果是同等的。

      <delete id="delete" >
          delete from t_car where id in 
          <foreach collection="ids" item="id" separator="," open="(" close=")">
              #{id}
          </foreach>
      </delete>
      
    • 批量插入数据示例:

      • dao接口

        public interface CarDao {
            int insert(@Param("cars") List<Car> cars);
        }
        
      • XML文件

        <insert id="insert">
            insert into t_car values
            <foreach collection="cars" item="car" separator=",">
                (null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
            </foreach>
        </insert>
        
    • 批量删除数据示例:

      • dao接口

        public interface CarDao {
            int delete(@Param("ids") Long ids[]);
        }
        
      • XML文件

        <delete id="delete" >
            delete from t_car where
              <foreach collection="ids" item="id" separator="or">
                  id = #{id}
              </foreach>
        </delete>
        
  8. sql标签和include标签

    • sql标签用来声明sql片段;include标签用来将声明的sql片段包含到某个sq|语句当中

    • 作用:代码复用、易维护。

    • 使用示例:

      <!-- 声明一个sql片段。 -->
      <sql id="carColumnNameSql">
          id,
          car_num as carNum,
          brand,
          guide_price as guidePrice,
          produce_time as produceTime,
          car_type as carType
      </sql>
      <select id="selectById2" resultType="car">
          select
          <!--refid指定sql标签的id -->
          <include refid="carColumnNameSql"/>
          from t_car where id = #{id}
      </select>
      

十二、MyBatis的高级映射及延迟加载

  1. 高级映射:其实就是多表操作。多张表数据映射到一个java对象。这里高级映射主要讲了两个方面,一对多映射和多对一映射

  2. 怎么分主表和副表?谁在前谁是主表。

    • 多对一:多在前,那么多就是主表。

    • 一对多:一在前,那么一就是主表。

    • 高级映射,多张表将被映射到一个java对象中,那么映射到哪一个对象中呢?会映射到对应主表的对象中。

  3. 多对一映射

    • 多对一映射,那么多的一方为主表。例如学生表和班级表,多个学生对应一个班级,也就是多对一的关系,那么学生表是多的一方,学生表就是主表,班级表就是副表。

    • 如果进行映射,将多张表映射到一个java对象中?有多种方式,常见的包括三种:

      • 第一种方式:一条SQL语句,级联属性映射

      • 第二种方式:一条SQL语句,association标签。

      • 第三种方式:两条SQL语句,分步查询。(这种方式常用:优点一是可复用。优点二是支持懒加载。)

  4. 多对一映射 ---- 级联属性映射

    • 级联属性映射:一条SQL 语句,级联属性映射。级联属性映射,就是利用resultMap标签对属性和字段进行映射,内部对象的所属属性也进行映射,而SQL语句就进行表的连接进行查询。

    • 示例:有两张表sutdent表和class表,student表和class表是多对一的关系。通过id查找student信息,包括班级信息

      • student类:在sudent类,也就是多的一方,添加一个属性指向一的一方。这里是Class clazz属性。

        public class Student {
            private Long sid;
            private String sName;
            private Class clazz;
        }
        
      • class类

        public class Class {
            private Long cid;
            private String cname;
        }
        
      • dao接口

        public interface StudentDao {
            public Student selectById(@Param("id") Long id);
        }
        
      • XML文件

        <resultMap id="student_Class" type="shh.pojo.Student">
            <result column="sid" property="sid"/>
            <result column="sname" property="sName"/>
            <result column="cid" property="clazz.cid"/>
            <result column="cname" property="clazz.cname"/>
        </resultMap>
        
        <select id="selectById" resultMap="student_Class">
            select s.*,c.*
                from students s left join class c on c.cid = s.cid
            where s.sid = #{id}
        </select>
        
  5. 多对一映射 ---- 一条sql语句,采用association标签来实现

    • association标签:翻译为关联。用于resultMap标签中,让一个java属性关联另一个类。

      • property:提供要映射的POJO类的属性名。

      • javaType:用来指定要映射的java类型。

    • 示例:有两张表sutdent表和class表,student表和class表是多对一的关系。通过id查找student信息,包括班级信息

      • dao接口

        public interface StudentDao {
            public Student selectById(@Param("id") Long id);
        }
        
      • XML文件

        <resultMap id="student_Class" type="shh.pojo.Student">
            <result column="sid" property="sid"/>
            <result column="sname" property="sName"/>
            <!--将Student类中的clazz属性关联到Class类。-->
            <association property="clazz" javaType="Class">
                <id property="cid" column="cid"/>
                <result property="cname" column="cname"/>
            </association>
        </resultMap>
        <select id="selectById" resultMap="student_Class">
            select s.*,c.*
                from students s left join class c on c.cid = s.cid
            where s.sid = #{id}
        </select>
        
  6. 多对一映射 ---- 两条sql语句,分步查询

    • 这种方式常用:

      • 复用性增强。可以重复利用。(大步拆成N多个小碎步。每一个小碎步更加可以重复利用。)

      • 采用这种分步查询,可以充分利用他们的延迟加载/懒加载机制。

    • 示例:有两张表sutdent表和class表,student表和class表是多对一的关系。通过id查找student信息,包括班级信息

    • 注意:依然会使用到association标签,会用到两个新的属性:cloum、select

      • 第一步:dao接口,查询出满足id=1l的student信息

        public interface StudentDao {
            public Student selectStep1(@Param("sid") Long id);
        }
        
      • 第二步:XML文件

        <resultMap id="student_Class" type="shh.pojo.Student">
            <result column="sid" property="sid"/>
            <result column="sname" property="sName"/>
            <!--将Student类中的clazz属性关联到select指定的selectId的查询结果中。-->
            <!--
             这里由于查找class的时候,需要使用cid作为参数,所以必须指定column=cid,
             就是表示将这个查询出来的属性值作为下一个查询的参数。
             -->
            <association property="clazz" select="shh.dao.ClassDao.selectStep2" column="cid"/>
        </resultMap>
        <select id="selectStep1" resultMap="student_Class">
            select sid,sname,cid from students where sid = #{sid}
        </select>
        
      • 第三步:通过查询出来的cid去查询班级信息,dao层:

        public interface ClassDao {
            Class selectStep2(@Param("cid") Long cid);
        }
        
      • 第四步:XML文件

        <mapper namespace="shh.dao.ClassDao">
            <select id="selectStep2" resultType="shh.pojo.Class">
                select * from class where cid = #{cid}
            </select>
        </mapper>
        
    • 上述整个代码流程的示意图

    • 什么是延迟加载(懒加载),有什么用?延迟加载的核心原理是:用的时候再执行查询语句。不用的时候不查询。可以提高性能

  7. 延迟加载

    • 注意:默认情况下是没有开启延迟加载的。

    • 在mybatis当中怎么开启延迟加载呢?

      • 一种方式是在association标签中添加fetchType="lazy"。这样的话只有association标签中涉及到的select属性指向的地方会进行懒加载,也就是局部懒加载。

      • 开启全局的懒加载:所有带有分步的,都采用延迟加载

        <settings>
            <!--延迟加载的全局开关。默认值false不开启。-->
            <!--什么意思:所有带有分步的,都采用延迟加载。-->
            <setting name="lazyLoadingEnabled" value="true"/>
        </settings>
        
    • 当开启全局懒加载的时候,也可以让某一条sql不懒加载,同样是利用association标签中的fetchType属性,设置其值为eager即可。实际开发中,一般会打开全局懒加载,然后哪个地方不需要使用懒加载的话,再具体设置即可。

    • 体会懒加载:

      • 在上面的分布查询的例子中,没有开启懒加载,默认是不开启的。那么执行以下测试代码:

        public static void main(String[] args) throws ParseException {
            SqlSession sqlSession = SqlSessionUtil.getSqlSession();
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            // 只需要使用student表中的sname属性。
            System.out.println(mapper.selectStep1(1l).getSName());
            
            
            sqlSession.commit();
            sqlSession.close();
        }
        
      • 执行结果分析:只需要使用student表中的sname属性,但是这个时候可以看到依然需要执行两条sql,也就是分步查询中的两个步骤都需要走一遍。但是其实第二步是不需要的,因为只需要student表的sname属性。

      • 开启懒加载

        <mapper namespace="shh.dao.StudentDao">
        
            <resultMap id="student_Class" type="shh.pojo.Student">
                <result column="sid" property="sid"/>
                <result column="sname" property="sName"/>
                <!--将Student类中的clazz属性关联到select指定的selectId的查询结果中。-->
                <!--开启懒加载,将fetchType设置为lazy-->
                <association property="clazz" select="shh.dao.ClassDao.selectStep2" column="cid" fetchType="lazy"/>
            </resultMap>
            <select id="selectStep1" resultMap="student_Class">
                select sid,sname,cid from students where sid = #{sid}
            </select>
        </mapper>
        
      • 再次执行测试代码,可以发现只执行了一条sql,第二步的sql不再执行,这就提高了效率。

  8. 一对多映射

    • 一对多映射,一是主表,所以通常是在一的一方中有List集合属性。例如一班级表class对应多个学生student,在Clazz类中添加Liststus;属性。

    • 一对多的实现通常包括两种实现方式:

      • 第一种方式:collection

      • 第二种方式:分步查询

  9. 一对多映射 --- collection

    • 示例:有两张表sutdent表和class表,student表和class表是多对一的关系。通过id查找student信息,包括班级信息

      • student类

        public class Student {
            private Long sid;
            private String sName;
        }
        
      • Class类:一对多,在一的一方添加一个List集合类型的属性,用来保存多的一方的对象。这里是List students属性。

        public class Clasz {
            private Integer cId;
            private String cName;
            private List<Students> studentsList;
        }
        
      • dao接口

        public interface ClaszDao {
            Clasz selectClaszById(@Param("cid") Integer cid);
        }
        
      • XML文件

        <resultMap id="class_students" type="clasz">
            <result column="cid" property="cId"/>
            <result column="cname" property="cName"/>
            <!--ofType属性用于指定集合中元素的类型-->
            <collection property="studentsList" ofType="students">
                  <id column="sid" property="sId"/>
                  <result column="sname" property="sName"/>
                  <result column="cid" property="cId"/>
            </collection>
        </resultMap>
        <select id="selectClaszById" resultMap="class_students">
            select c.*,s.* from class c left join students s
                     on c.cid = s.cid
                     where c.cid = #{cid}
        </select>
        
  10. 一对多映射 —— 分步查询

    • 需要使用collection标签:在resultMap中使用collection标签,类似association标签那样,属性:

      • ofType属性:用来指定集合当中的元素类型

      • property属性:主表中的属性。

    • 示例:有两张表sutdent表和class表,student表和class表是多对一的关系。通过id查找student信息,包括班级信息

      • 第一步:dao接口,查询出满足cid=1000l的class信息

        public interface ClassDao {
            Class selectById(@Param("cid") Long cid);
        }
        
      • 第一步:XML文件

        <resultMap id="class_students" type="class">
            <id column="cid" property="cid"/>
            <result column="cname" property="cname"/>
            <collection property="students" select="shh.dao.StudentDao.selectByCid" column="cid">
                <id column="sid" property="sid"/>
                <result column="sname" property="sName"/>
            </collection>
        </resultMap>
        <select id="selectById" resultMap="class_students">
            select cid,cname from class where cid = #{cid};
        </select>
        
      • 第二步:通过查询出来的cid去查询学生信息,dao层:

        public interface StudentDao {
            List<Student> selectByCid(@Param("cid") Long Cid);
        }
        
      • 第二步:XML文件

        <mapper namespace="shh.dao.StudentDao">
            <select id="selectByCid" resultType="shh.pojo.Student">
                select * from students where cid = #{cid}
            </select>
        </mapper>
        
  11. 此外还有一对一、多对多关系的映射,尝试实现。

十三、MyBatis的缓存

  1. MyBatis的缓存机制:执行DQL(select语句)的时候,将查询结果放到缓存当中(内存当中),如果下一次还是执行完全相同的语句,直接从缓存中拿数据。不再查数据库了。不再去硬盘上找数据了。知道数据被修改了,mybatis才会清理缓存,重新去数据库中获取数据。在这样的机制下,可以使用减少IO的频率,减少了项目进行硬盘操作的频率,从而提高效率。

  2. mybatis缓存包括:缓存只针对于DQL语句,也就是说缓存机制只对应select语句。

    • 一级缓存:将查询到的数据存储到Sqlsession中。(注意考虑SqlSession和SqlSessionFactory的生存周期)

    • 二级缓存:将查询到的数据存储到SqlSessionFactory中。

    • 或者集成其它第三方的缓存:比如EhCache【Java语言开发的]、Memcache[C语言开发的]等。

  3. 一级缓存:缓存在SqlSession中。

    • 一级缓存默认是开启的。不需要做任何配置。只要使用同一个SqlSession对象执行同一条SQL语句,就会走缓存。

    • 思考:什么时候不走缓存?SqlSession对象不是同一个,肯定不走缓存; 查询条件不一样,肯定也不走缓存。

    • 思考:什么时候一级缓存失效? 执行了sqlSession的clearCache()方法,这是手动清空缓存;执行了INSERT或DELETE或UPDATE语句。不管你是操作哪张表的,都会清空一级缓存,也就是只要SqlSession对象执行了修改数据的操作,就会清理SqlSession的缓存。

  4. 二级缓存:

    • 二级缓存的范围是SqlSessionFactory

    • 使用二级缓存需要具备以下几个条件:

      • 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认就是true,无需设置。

        <setting name="cacheEnabled" value="true">:
        
      • 默认情况下,二级缓存机制是开启的。只需要在对应的SqlMapper.xml文件中添加以下标签。用来表示“我"使用该二级缓存即可。

        <cache/>
        
      • 使用二级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接口

      • SqlSession对象关闭或提交之后,一级缓存中的数据才会被写入到二级缓存当中。此时二级缓存才可用。(李姐:如果SqlSession对象还在, 那么直接使用SqlSession对象的一级缓存即可,不需要使用到二级缓存)

    • 二级缓存的失效:只要两次查询之间出现了增删改操作,二级缓存就会失效。【一级缓存也会失效]

  5. 二级缓存的相关配置:

    • eviction:指定从缓存中移除某个对象的淘汰算法。默认采用LRU策略。

      • LRU:Least Recently Used。最近最少使用。优先淘汰在间隔时间内使用频率最低的对象。(其实还有一种淘汰算法LFU,最不常用。)

      • FIFO: First In First Out。一种先进先出的数据缓存器。先进入二级缓存的对象最先被淘汰。

      • SOFT:软引用。淘汰软引用指向的对象。具体算法和JVM的垃圾回收算法有关。

      • WEAK:弱引用。淘汰弱引用指向的对象。具体算法和JVM的垃圾回收算法有关。

    • flushlnterval:二级缓存的刷新时间间隔。单位毫秒。如果没有设置。就代表不刷新缓存,只要内存足够大,一直会向二级缓存中缓存数据。除非执行了增删改。

    • readOnly:

      • true: 多条相同的sql语句执行之后返回的对象是共享的同一个。性能好。但是多线程并发可能会存在安全问题。

      • false: 多条相同的sql语句执行之后返回的对象是副本,调用了clone方法。性能一般。但安全。

    • size:设置二级缓存中最多可存储的java对象数量。默认值1024。

  6. MyBatis集成EhCache

    • 集成EhCache是为了代替mybatis自带的二级缓存。一级缓存是无法替代的。

    • mybatis对外提供了接口,也可以集成第三方的缓存组件。比如EhCache、Memcache等。都可以。

    • EhCache是Java写的。Memcache是C语言写的。所以mybatis集成EhCache较为常见,按照以下步骤操作,就可以完成集成

    • 第一步:引入mybatis整合ehcache的依赖。

      <!--mybatis集成ehcache的组件-->
      <dependency>
          <groupId>org.mybatis.caches</groupId>
          <artifactId>mybatis-ehcache</artifactId>
          <version>1.2.2</version>
      </dependency>
      
    • 在类的根路径下新建echcache.xml文件,并提供以下配置信息。

      <?xml version="1.8"
              encoding="UTF-8"?>
      <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
               updateCheck="false">
          <!--磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存-->
          <diskStore path="e:/ehcache"/>
          <!--defaultCache:默认的管理策略-->
          <!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断-->
          <!--maxElementsInMemory:在内存中缓存的element的最大数目-->
          <!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上-->
          <!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false-->
          <!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问-->
          <!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问-->
          <!--memoryStoreEvictionPolicy:缓存的3种清空策略-->
          <!--FIFO: first in first out (先进先出)-->
          <!--LFU: Less Frequently Used(最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit属性,hit值最小的将会被清出缓存-->
          <!--LRU: Least Recently Used(最近最少使用).(ehcache默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当
          <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
          timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
          -->
      </ehcache>
      
    • 第三步:修改SqlMapper.xml文件中的cache标签,添加type属性。

      <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
      

十四、MyBatis的逆向工程

  1. 所谓的逆向工程是:根据数据库表逆向生成Java的pojo类,SqlMapper.xml文件,以及Mapper接口类等。要完成这个工作,需要借助别人写好的逆向工程插件。

  2. 第一步:配置插件

    <!-- mybatis逆向工程依赖 -->
    <dependency>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-core</artifactId>
        <version>1.3.7</version>
    </dependency>
    <!--配置mybatis逆向工程的插件-->
    <!--定制构建过程-->
    <build>
      <plugins>
        <!-- 自动生成mybatis-generator-core依赖引入(核心) -->
        <plugin>
          <groupId>org.mybatis.generator</groupId>
          <artifactId>mybatis-generator-maven-plugin</artifactId>
          <version>1.3.2</version>
          <!--允许覆盖-->
          <configuration>
            <overwrite>true</overwrite>
          </configuration>
          <dependencies>
            <dependency>
              <groupId>org.mybatis.generator</groupId>
              <artifactId>mybatis-generator-core</artifactId>
              <version>1.3.2</version>
            </dependency>
          </dependencies>
        </plugin>
      </plugins>
    </build>
    

  3. 第二步:配置generatorConfig.xml。该文件名必须叫做:generatorConfig.xml,该文件必须放在类的根路径下。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
            "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <generatorConfiguration>
        <!-- 
        targetRuntime有两个值:
        MyBatis3Simple:生成的是基础版,只有基本的增删改查。
        MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。
        -->
        <context id="context1" target="MyBatis3Simple">
            <!--防止生成重复代码-->
            <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
            <commentGenerator>
                <!--是否去掉生成日期-->
                <property name="suppressDate" value="true"/>
                <!--是否去除注释-->
                <property name="suppressAllComments" value="true"/>
            </commentGenerator>
     
             <!-- 数据库信息 -->
            <jdbcConnection connectionURL="jdbc:oracle:thin:@//127.0.0.1:1521/LOUTE"
                            driverClass="oracle.jdbc.driver.OracleDriver" 
                            password="scott"
                            userId="scott">
                <!-- 针对oracle数据库 -->
                <property name="remarksReporting" value="true"></property>
            </jdbcConnection>
     
            <!-- 配置pojo生成的位置 -->
            <javaModelGenerator targetPackage="com.自己的.demo.pojo"  targetProject="自己的/src/main/java">
                <!--是否开启子包-->
                <property name="enableSubPackages" value="true" />
                <!--是否去除字段名的前后空白-->
                <property name="trimStrings" value="true" />
            </javaModelGenerator>
     
     
            <!-- 配置sql映射文件的生成位置 -->
            <sqlMapGenerator targetPackage="mapper" targetProject="自己的/src/main/resources">
                <property name="enableSubPackages" value="true" />
                <property name="trimStrings" value="true" />
            </sqlMapGenerator>
     
            <!-- 生成Mapper接口的包名和位置-->
            <javaClientGenerator
                type="xMapper"
                targetPackage="com.powernode.mybatis.mapper"
                targetProject="src/main/java">
                <property name="enableSubPackages" value="true"/>
            </javaClientGenerator>
            <!-- 表名和对应的实体类名-->
            <table tableName="t_car1" domainObjectName="Car1"/>
            <table tableName="t_car2" domainObjectName="Car2"/>
            <table tableName="t_car3" domainObjectName="Car3"/>
        </context>
    </generatorConfiguration>
    
  4. 执行插件即可。

十五、MyBatis使用PageHelper

  1. PageHelper:用于实现分页的插件

  2. 第一步:引入依赖

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.3.1</version>
    </dependency>
    
  3. 第二步:在mybatis-config.xml文件中配置插件,在typeAliases标签下面进行配置:

    <!--mybatis分页的拦截器-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
    
  4. 第三步:写代码使用这个组件了。

    • mapper.xml文件

      <mapper namespace="com.powernode.mybatis.mapper.CarMapper">
        <select id="selectall" resultType="Car">
            select * from t_car
        </select>
      </mapper>
      
    • 基本使用:

      @Test
      public void testSelectAll(){
          SqlSession sqlSession = SqlSessionUtil.openSession();
          CarMapper mapper = sqlSession.getMapper(CarMapper.class);
          // 一定一定一定要注意:在执行DQL语句之前。开启分页功能。
          int pageNum = 2;
          int pageSize = 3;
          PageHelper.staritPage(pageNum, pageSize);
      
          List<Car> cars = mapper.selectAll();
          cars.forEach(car -> System.out.println(car));
          sqlSession.close();
      }
      
    • 获取分页之后的分页数据:PageInfo<?> 对象封装如当前页、是否有下一页、是否有上一页、当前页数据量、上一页页面、是否是最后一页、是否是第一页等等贼多。

      @Test
      public void testPageHelper() throws Exception{
          SqlSessionFactorysqlSessionFactory = new   SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
          SqlSession sqlSession =sqlSessionFactory.openSession();
          CarMapper mapper = sqlSession.getMapper(CarMapper.class);
          // 开启分页
          PageHelper.startPage(2,2);
          // 执行查询语句
          List<Car> cars = mapper.selectAll();
          // 获取分页信息对象:5表示当前页数
          PageInfo<Car> pageInfo = new PageInfo<>(cars, 5);
          System.out,println(pageInfo);
      }
      

十六、MyBatis的注解式开发

  1. mybatis中也提供了注解式开发方式,采用注解可以减少Sql映射文件的配置,使用注解代替xml文件。当然,使用注解式开发的话,sql语句是写在java程序中的,这种方式也会给sql语句的维护带来成本。官方是这么说的:

    使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java注解不仅力不从心,还会让你本就复杂的SQL语句更加混乱不堪。因此,如果你需要做一些很复杂的操作,最好用XML来映射语句。

  2. 需要注意:使用这个的时候,需要将核心配置文件中的mappers映射写为以下方式:

    <mappers>
        <package name="shh.dao"/>   
    </mappers>
    
  3. 直接上使用示例:

    • 环境的话就还是正常的mybatis的使用环境即可。需要mybatis核心配置文件、

    • 示例:

      public interface CarDao {
          @Delete("delete from t_car where id=#{id}")
          int delete(@Param("id") Long id);
      
          @Update("update t_car set guide_price = #{guidePrice} where id = #{id}")
          int update(@Param("car") Car car);
      
          @Select("select * from t_car where id = #{id}")
          @ResultType(Car.class)
          Car selectById(@Param("id") Long id);
      
          // 是用类似resultMap标签的注解
          @Select("select * from t_car where id = #{id}")
          @Results({
                  @Result(column = "id",property = "id"),
                  @Result(column = "id",property = "id"),
                  @Result(column = "id",property = "id"),
                  @Result(column = "id",property = "id")
          })
          Car selectByIdOtherName(@Param("id") Long id);
      }
      
  4. 最佳实战:使用XML配置文件来实现繁琐的操作,使用注解来实现简单的crud操作。

posted @ 2026-03-10 18:45  哈哈嗨  阅读(9)  评论(0)    收藏  举报