[学习笔记] MyBatis之SQL映射文件

# 学习 # · 2020-11-19

使用MyBatis实现条件查询

1、SQL映射文件的几个顶级元素(按照定义的顺序):

(1)mapper:映射文件的根元素节点,只有一个属性namespace(命名空间),用于区分不同的mapper,全局唯一,绑定DAO接口(必须和接口同名)。

(2)cache:配置给命名空间的缓存。

(3)cache-ref:从其他命名空间引用缓存配置。

(4)resultMap:用来描述数据库结果集和对象的对应引用。

(5)sql:可以重用的SQL块,也可以被其他语句引用。

(6)insert/delete/update/select:映射插入/删除/更新/查询语句,增删改操作时需要提交事务。

2、使用select完成单条件查询:

<!-- 根据用户名称查询用户列表(模糊查询) -->
<select id="getUserListByName" resultType="User" parameterType="String">
    select * from user where name like CONCAT ('%',#{name},'%')
</select>

(1)id:命名空间中唯一的标识符,可以被用来引用这条语句。

(2)parameterType:表示查询语句传入参数的类型的完全限定名或别名。

  ①基础数据类型(int、String、Date等):只能传入一个,通过#{参数名}即可获取传入的值。

  ②复杂数据类型(Java实体类、Map等):通过#{属性名}@{map的keyName}即可获取传入的值。

(3)resultType:查询语句返回结果类型的完全限定名或别名。

3、使用select完成多条件查询:

(1)方法一:将查询条件封装成对象进行入参:

//改造UserMapper.java
public interface UserMapper {
    /**
     * 查询用户列表(参数:对象入参)
     * @return
     */
    public List<User> getUserList(User user);
}
<!-- 改造UserMapper.xml -->
<!-- 查询用户列表(参数:对象入参) -->
<select id="getUserList" resultType="User" parameterType="User">
    select * from user where name like CONCAT ('%',#{name},'%') and pwd = #{pwd}
</select>
//改造测试类
User user = new User();
user.setUserName("赵");
user.setUserRole(3);
userList = sqlSession.getMapper(UserMapper.class).getUserList(user);

(2)方法二:将查询条件封装成Map对象进行入参:

//改造UserMapper.java
public interface UserMapper {
    /**
     * 查询用户列表(参数:Map)
     * @return
     */
    public List<User> getUserListByMap(Map<String, String> userMap);
}
<!-- 改造UserMapper.xml -->
<!-- 查询用户列表(参数:Map) -->
<select id="getUserListByMap" resultType="User" parameterType="Map">
    select * from smbms_user
    where userName like CONCAT ('%',#{uName},'%') and userRole = #{uRole}
</select>
//改造测试类
Map<String, String> userMap = new HashMap<String, String>();
userMap.put("uName", "赵");
userMap.put("uRole", "3");
userList = sqlSession.getMapper(UserMapper.class).getUserListByMap(userMap);

4、使用resultMap完成查询结果的展现:

(1)在User类中加入属性,并实现getter/setter方法:

private String newName; //用户新的名称

(2)在UserMapper.xml中,修改getUserList的SQL映射语句,并修改select的resultType属性为resultMap。

<!-- 查询用户列表(参数:对象入参) -->
<select id="getUserList" resultMap="userList" parameterType="User">
    select * from user where id=#{id}
</select>

(3)在UserMapper.xml中增加id为userList的resultMap元素节点:

<!--
    id:唯一标识,此id值用于select元素resultMap属性的引用
    type属性:表示该resultMap的映射结果类型
    result子节点:用于表示一些简单的属性
    column:数据库据中查询的字段名
    property:查询出来的字段对应的值赋给实体对象的哪个属性
-->
<resultMap type="User" id="userList">
    <id column="id" property="id" />
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!-- 用户新的名称:在User实体对象中是newName,在数据库中是name -->
    <result property="newName" column="name"/>
</resultMap>

5、resultType和resultMap:

(1)resultType映射的应用场景:简单数据类型、简单自定义

(2)resultMap映射的应用场景:返回列名和属性名不一致时;多表连接查询时。

(3)在MyBatis的select元素中,resultType和resultMap本质上都是一样的,都是Map数据结构,但这两个属性不能同时存在。


使用MyBatis实现增删改操作

1、使用insert完成增加操作:

(1)修改Mapper文件,加入insert节点。

<insert id="addUser" parameterType="com.mybatis.pojo.User">
    insert into user(id,name,pwd) values(#{id},#{name},#{pwd});
</insert>

(2)修改测试类:注意增删改操作时需要提交事务。

@Test
public void addUser() {
    //第一步:获取SqlSession对象
    SqlSession sqlSession = MyBatisUtil.createSqlSession();

    //第二步:实例化一个User信息
    User user = new User();
    user.setId(3);
    user.setName("demo");
    user.setPwd("123456");

    //第二步:执行SQL语句
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    userMapper.addUser(user);

    //第四步:提交事务。事物回滚:sqlSession.rollback();
    sqlSession.commit();

    //第五步:关闭资源
    MyBatisUtil.closeSqlSession(sqlSession);
}

2、使用update完成修改操作:

<!-- 修改用户信息 -->
<update id="editUser" parameterType="com.mybatis.pojo.User">
    update user set name=#{name} where id=#{id};
</update>

3、使用delete完成删除操作:

<!-- 根据id删除用户信息 -->
<delete id="delUser" parameterType="int">
    delete from user where id=#{id};
</delete>

4、使用@Param注解实现多参数入参:

(1)实现修改密码的方法,当方法参数有多个时,每个参数前都需要增加@Param注解:

public int updatePwd(@Param('id')Integer id, @Param('userPassword')String pwd);

(2)实现id为updatePwd的SQL映射:

<update id="updatePwd">
    <!-- 使用#{注解名称}读取 -->
    update user set pwd=#{userPassword} where id=#{id}
</update>

(3)注意:一般超过4个以上的参数最好封装成对象入参;对于参数固定的业务方法最好使用多参数入参。

(4)注意:当参数为基础数据类型时,无论是多参数入参,还是单独一个参数入参,都要使用@Param注解来进行参数传递。


复杂查询

1、resultMap元素的基本配置项:

(1)属性:

id:resultMap的唯一标识。
type:表示该resultMap的映射结果类型,通常是Java实体类。

(2)子节点:

id:一般对应数据库中该行的主键id。
result:映射到JavaBean的某个简单类型属性,如基础数据类型、包装类等。
association(多对一):映射到JavaBean的某个“复杂类型”属性,比如JavaBean类。
collection(一对多):映射到JavaBean的某个“复杂类型”属性,比如集合

2、复杂查询环境搭建:

(1)创建数据库环境:

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '王老师');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

(2)数据库分析:

  ①对于student而言,多个student关联一个teacher(多对一)。

  ②对于teacher而言,一个teacher集合很多student(一对多)。

(3)新建实体类Teacher、Student。

public class Student {

    private long id;
      private String name;
    private int tid;
      private Teacher teacher;    //每个student关联的teacher实体
      //省略部分代码

}

public class Teacher {

      private long id;
      private String name;
    private List<Student> student;    //每个teacher的student集合
    //省略部分代码
}

(4)新建Mapper接口和Mapper.xml文件,在核心配置文件中绑定注册Mapper。

<mappers>
    <mapper class="com.mybatis.dao.StudentMapper"></mapper>
    <mapper class="com.mybatis.dao.TeacherMapper"></mapper>
</mappers>

(5)在TeacherMapper接口中添加方法,实现查询老师信息。

public interface TeacherMapper {
    @Select("select * from teacher where id=#{tid}")
    public Teacher getTeacher(@Param("tid") int id);
}

(6)创建测试类进行测试。

@Test
public void getTeacher() {
    SqlSession sqlSession = MyBatisUtil.createSqlSession();
    TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher result = teacherMapper.getTeacher(1);
    System.out.println("查询的老师的名字是" + result.getName());
}

3、多对一的处理:查询所有的学生信息及其对应的老师的信息。

(1)在StudentMapper接口中添加方法,实现查询所有的学生信息以及对应的老师的信息。

public interface StudentMapper {
    public List<Student> getStudent();
}

(2)编写Mapper方法一:按查询嵌套处理(类似于子查询)。

<!--
    实现思路:
    ①查询所有的学生信息
    ②根据查询出来的学生的tid,寻找对应的老师信息
-->
<mapper namespace="com.mybatis.dao.StudentMapper">
    <!--select查询student-->
    <select id="getStudent" resultMap="stInfo">
        select * from student;
    </select>

    <resultMap id="stInfo" type="com.mybatis.pojo.Student">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <!--
            复杂的属性需要单独处理
            对象:association
            集合:collection
        -->
        <association property="teacher" column="tid" select="getTeacher"></association>
    </resultMap>
    <select id="getTeacher" resultType="com.mybatis.pojo.Teacher">
        select * from teacher where id=#{tid}
    </select>
</mapper>

(3)编写Mapper方法二:按照结果嵌套处理(类似于联表查询)。

<mapper namespace="com.mybatis.dao.StudentMapper">
    <select id="getStudent" resultMap="stInfo">
        select s.id as sid, s.name as sname, t.name as tname  from student as s, teacher as t where s.tid=t.id;
    </select>
    <resultMap id="stInfo" type="com.mybatis.pojo.Student">
        <result property="id" column="sid"></result>
        <result property="name" column="sname"></result>
        <association property="teacher" javaType="com.mybatis.pojo.Teacher">
            <result property="name" column="tname"></result>
        </association>
    </resultMap>
</mapper>

(4)创建测试类:

StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> result = studentMapper.getStudent();
for(Student stu : result) {
    System.out.println("学生" + stu.getName() + "的老师是" + stu.getTeacher().getName());
}

4、一对多的处理:查询老师下面的学生集合。

(1)在TeacherMapper接口中添加方法,实现查询指定的老师信息和学生集合。

public interface TeacherMapper {
    public List<Teacher> getTeacher(@Param('tid') int tid);
}

(2)编写Mapper方法一:按查询嵌套处理(类似于子查询)。

<!--
    实现思路:
    ①查询所有的学生信息
    ②根据查询出来的学生的tid,寻找对应的老师信息
-->
<mapper namespace="com.mybatis.dao.StudentMapper">
    <!--select查询student-->
    <select id="getStudent" resultMap="stInfo">
        select * from student;
    </select>

    <resultMap id="stInfo" type="com.mybatis.pojo.Student">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <!--
            复杂的属性需要单独处理
            对象:association
            集合:collection
        -->
        <association property="teacher" column="tid" select="getTeacher"></association>
    </resultMap>
    <select id="getTeacher" resultType="com.mybatis.pojo.Teacher">
        select * from teacher where id=#{tid}
    </select>
</mapper>

(3)编写Mapper方法二:按照结果嵌套处理(类似于联表查询)。

<mapper namespace="com.mybatis.dao.TeacherMapper">
    <select id="getTeacher" resultMap="tsInfo">
        select s.id as sid, s.name as sname, t.name as tname,t.id as tid
        from student as s,teacher as t
        where s.tid = t.id and t.id = #{tid}
    </select>

    <resultMap id="tsInfo" type="com.mybatis.pojo.Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!-- 集合中的泛型信息,我们使用ofType获取 -->
        <collection property="student" ofType="com.mybatis.pojo.Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
</mapper>

(4)创建测试类:

TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> result = teacherMapper.getTeacher(1);
for(Teacher tch : result) {
    System.out.println("老师" + tch.getName() + "的学生数量是" + tch.getStudent().size());
}

5、javaType和ofType:

(1)javaType:用来指定实体类中属性的类型。

(2)ofType:用来指定映射到List或者集合中的 pojo类型,泛型中的约束类型。


resultMap自动映射级别和MyBatis缓存

1、resultMap的自动映射级别:

<!-- 修改配置文件mybatis-config.xml -->
<settings>
    <!-- 
    设置resultMap的自动映射级别
    NONE:禁止自动匹配
    PARTIAL(默认):自动匹配所有属性,有内部嵌套(association、collection)的除外
    FULL:自动匹配所有
    -->
    <setting name="autoMappingBehavior" value="级别值" />
</settings>

2、缓存简介:

(1)缓存(Cache):即存在内存中的临时数据。

(2)为什么要使用缓存:少和数据库的交互次数,减少系统开销,提高系统效率。

(3)经常查询并且不经常改变的数据可以使用缓存。

3、MyBatis缓存:

(1)默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)

(2)二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

(3)为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。

4、一级缓存:与数据库同一次会话期间查询到的数据会放在本地缓存中。

(1)基于PerpetualCache(MyBatis自带)的HashMap本地缓存,作用范围为session域内。

(2)一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段。一级缓存就是一个Map。

5、二级缓存:也叫全局缓存,基于namespace级别的缓存,一个名称空间对应一个二级缓存。

(1)开启缓存:修改MyBatis核心配置文件。

<settings>
    <setting name="cacheEnabled" value="true" />
</settings>

(2)在要使用二级缓存的Mapper中开启。

<!--
在Mapper中设置缓存,默认情况下没有开启缓存
只有在com.mybatis.dao.UserMapper的查询才能共享这个cache
-->
<mapper namespace="com.mybatis.dao.UserMapper">
    <cache eviction="FIFO"flushInterval="60000" size="512" readOnly="true"/>
</mapper>

(3)Mapper文件配置支持cache后,如果需要对个别查询进行调整,可以单独设置cache。

<select id="selectAll" resultType="Emp" useCache="true">
</select>

使用注解开发

1、使用注解实现CRUD:

public interface UserMapper {

    @Select("select * from user")
    List<User> getUsers();

    // 方法存在多个参数,所有的参数前面必须加上 @Param("id")注解
    @Select("select * from user where id = #{id}")
    User getUserByID(@Param("id") int id);

    @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
    int addUser(User user);

    @Update("update user set name=#{name},pwd=#{password} where id = #{id}")
    int updateUser(User user);

    @Delete("delete from user where id = #{uid}")
    int deleteUser(@Param("uid") int id);

}

日志工厂

1、如果一个数据库操作出现异常,我们需要进行排错,日志就是最好的助手,因此需要日志工厂来辅助。

2、logImpl:指定 MyBatis 所用日志的具体实现,未指定时将自动查找。其类型有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING。

3、STDOUT_LOGGING标准日志输出:

(1)修改MyBatis核心配置文件:

<settings>
    <!-- 标准日志工厂实现 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

(2)查看输出的日志。

4、LOG4J日志输出:

(1)导入LOG4J的依赖包:修改pom.xml

<!--LOG4J-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

(2)创建LOG4J配置文件(log4j.properties):

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=log.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

(3)修改MyBatis核心配置文件,配置log4j为日志的实现:

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

(4)查看输出的日志。

(5)LOG4J的简单使用:

//在需要使用LOG4J的类中导入包
import org.apache.log4j.Logger;

//创建日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserDaoTest.class);

//不同日志级别的输出
logger.info("info:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
logger.error("error:进入了testLog4j");
如无特殊说明,本博所有文章均为博主原创。

如若转载,请注明出处:一木林多 - https://www.l5v.cn/archives/188/

评论