MyBatis 第二扇门

一、日志

1.日志工厂

如果一个数据库操作出现了异常,我们需要排错。
在这里插入图片描述
STDOUT_LOGGING标准日志输出

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

设置即可使用。这样在执行时即可打印日志。
在这里插入图片描述

2.LOG4J

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  1. maven中导入包
  2. 写配置文件 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/dlc.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
  1. 配置log4j为日志实现
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

log4j的使用

  1. 导入包(别导错了)
  2. 日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
  1. 使用
    @Test
    public void testLog4j(){
        logger.info("info:进入了testLog4j");
        logger.debug("debug:进入了testLog4j");
        logger.error("error:进入了testLog4j");
    }

二、分页

为什么要分页?

  • 减少数据的处理量

1.limit

select * from user limit startIndex,pageSize;
使用MyBatis实现分页
//接口
List<User> getUserByLimit(Map<String,Integer> map);

实现

    <select id="getUserByLimit" parameterType="map" resultType="User">
        select * from user limit #{startIndex},#{pageSize}
    </select>

测试

	@Test
    public void getUserByLimit(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String,Integer> map = new HashMap<String, Integer>();
        map.put("startIndex",0);
        map.put("pageSize",2);
        List<User> users = userMapper.getUserByLimit(map);
        for (User user : users) {
            System.out.println(user);
        }

        sqlSession.close();
    }

2.RowBounds(不推荐)

3.分页插件 PageHelper

三、使用注解开发

1.面向接口编程

真正的开发中,大多都会选择面向接口编程

根本原因:解耦,可拓展,提高复用。分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好。

关于接口的理解

  • 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离
  • 接口的本身反映了系统设计人员对系统的抽象理解
  • 接口应有两类:第一类是对一个体的抽象,它可对应为一个抽象体(abstract class);
  • 第二类是对一个体某一方面的抽象,即形成一个抽象面(interface);
    一个体有可能有多个抽象面。

2.使用注解开发

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

在配置文件中绑定接口:

    <mappers>
        <mapper class="com.dlc.dao.UserMapper"/>
    </mappers>

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

本质:反射机制
底层:动态代理

3.使用注解完成CRUD

在工具类中,将openSession方法的参数设为true,可以实现自动提交事务

    public  static SqlSession getSqlSession(){
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        return sqlSession;
    }
    //根据id查询用户
    @Select("select * from user where id=#{id}")
    User getUserById(@Param("id") int id);

sql语句中的参数名在@Param中取。

至于其他的语句,都是一样的,不再列举。

关于@Param
  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型的话,可以忽略,但是建议加上

四、Lombok

自动生成get、set、toString、equals等方法,不用手动编写

  1. 在idea中安装Lombok插件
  2. 导入lombok依赖
  3. @Data get set toString hashcode equals
    @AllArgsConstructor 有参构造
    @NoArgsConstructor 无参构造

五、多对一处理

多对一:多个学生对应一个老师 关联
一对多:一个老师拥有多个学生 集合

数据库表准备

create table teacher(
	id int(10) not null primary key,
	name varchar(30) default null
)engine=innodb default charset=utf8;

insert into teacher(id,name) values(1,"秦老师");

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 student values (1,"小明",1);
insert into student values (2,"小红",1);
insert into student values (3,"小张",1);
insert into student values (4,"小李",1);
insert into student values (5,"小王",1);

搭建好环境

将Mapper.xml放在resource下时,要保证包名和java包下的相同,并且使用class的方式在mybatis-config.xml中注册。

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

学生类:

@Data
public class Student {
    private int id;
    private String name;
    private Teacher teacher;
}

老师类

@Data
public class Teacher {
    private int id;
    private String name;
}

多对一的处理

(1)按照查询嵌套处理

查询所有学生信息以及对应的老师信息。
思路:

  1. 查询所有学生信息
  2. 根据学生的tid寻找对应的老师
  3. 写一个resultMap将Student与Teacher关联起来。
<mapper namespace="com.dlc.dao.StudentMapper">

    <resultMap id="StudentTeacher" type="Student">
        <result property="name" column="name"/>
        <result property="id" column="id"/>
        <!--复杂的对象需要单独处理
            对象:association
            集合:collection
        -->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher" parameterType="int">
        select * from teacher where id=#{id}
    </select>
    <select id="getStudent" resultMap="StudentTeacher">
        select * from student
    </select>
(2)按照结果嵌套处理(更加简单)
<mapper namespace="com.dlc.dao.StudentMapper">

    <select id="getStudent" resultMap="StudentTeacher">
        select s.id sid,s.name sname,t.name tname
        from student s,teacher t
        where s.tid=t.id
    </select>

    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>
</mapper>

六、一对多处理

学生类:

@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}

老师类:

@Data
public class Teacher {
    private int id;
    private String name;

    //一个老师有多个学生
    private List<Student> students;
}

1.按照结果嵌套(推荐)

<!--    按结果嵌套查询-->
    <select id="getTeacherById" resultMap="TeacherStudent" parameterType="int">
        select s.id sid,s.name sname,t.name tname,t.id tid
        from student s,teacher t
        where s.tid=t.id and t.id=#{tid}
    </select>
    <resultMap id="TeacherStudent" type="Teacher">
        <result property="name" column="tname"/>
        <result property="id" column="tid"/>
        <!--返回一个List,使用ofType-->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

按照查询嵌套(子查询)

    <select id="getTeacherById" resultMap="TeacherStudent">
        select * from teacher where id = #{id}
    </select>
    <resultMap id="TeacherStudent" type="Teacher">
        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId"/>
    </resultMap>
    <select id="getStudentByTeacherId" resultType="Student">
        select * from student where tid=#{tid}
    </select>

小结

  1. 关联-association
  2. 集合-collection
  3. javaType $ ofType
    1. javaType 用来指定实体类中属性的类型
    2. ofType 用来指定映射到List或者集合中的pojo类型

七、动态SQL

什么是动态SQL:

  • 根据不同的条件生成不同的sql语句。
    就是在拼接SQL语句,在保证SQL的正确性前提下,按照SQL格式,去组合

搭建环境

数据库表准备

create table blog(
	id varchar(50) not null,
	title varchar(100) not null,
	author varchar(30) not null,
	create_time datetime not null,
	views int(30) not null
)engine=innodb default charset=utf8;

实体类:

@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    //需要在配置文件中开启驼峰映射
    private Date createTime;
    private int views;
}

插入数据:

    public void insertData(){
        SqlSession session = MybatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        mapper.addBook(new Blog(IDUtils.getId(),"MyBatis如此简单","欻欻",new Date(),77));
        mapper.addBook(new Blog(IDUtils.getId(),"Java如此简单","欻欻",new Date(),99));
        mapper.addBook(new Blog(IDUtils.getId(),"Spring如此简单","欻欻",new Date(),99));
        mapper.addBook(new Blog(IDUtils.getId(),"MySQL如此简单","欻欻",new Date(),100));
        mapper.addBook(new Blog(IDUtils.getId(),"MyBatis如此简单","丁丁",new Date(),77));
        mapper.addBook(new Blog(IDUtils.getId(),"Java如此简单","丁丁",new Date(),99));
        mapper.addBook(new Blog(IDUtils.getId(),"Spring如此简单","丁丁",new Date(),99));
        mapper.addBook(new Blog(IDUtils.getId(),"MySQL如此简单","丁丁",new Date(),100));
        session.close();
    }

1.if语句

假如要实现一个功能,当传入标题按照标题模糊搜索,传入作者则按照作者模糊搜索,都传入就按照他们进行模糊搜索,否则全部查出。
接口:

    /**
     * 当传入title,根据title查询博客,传入author根据author查询博客,什么都不传查询全部博客
     * @param map
     * @return
     */
    List<Blog> queryBlogIF(Map map);

实现:

    <select id="queryBlogIF" resultType="com.dlc.pojo.Blog" parameterType="map">
        select * from blog where 1=1
        <if test="title != null">
            and title like "%"#{title}"%"
        </if>
        <if test="author != null">
            and author like "%"#{author}"%"
        </if>
    </select>

测试类:

    @Test
    public void queryData(){
        SqlSession session = MybatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        HashMap<String,Object> map = new HashMap<String, Object>();
        map.put("title","MySQL");
        map.put("author","欻欻");
        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        session.close();
    }
    //Blog(id=076fc171dbf242f380d4e471902f58bc, title=MySQL如此简单, author=欻欻, createTime=Tue Apr 07 10:32:24 CST 2020, views=100)

2.choose,when,otherwise

像是java中的switch,case,default。

假如要实现功能,要么按标题模糊搜索,要么按作者模糊搜索,要么全部查出。

接口:

    List<Blog> queryBlogChoose(Map map);

实现:

    <select id="queryBlogChoose" resultType="com.dlc.pojo.Blog">
        select * from blog where 1=1
        <choose>
            <when test="title!=null">
                and title like "%"#{title}"%"
            </when>
            <when test="author!=null">
                and author like "%"#{author}"%"
            </when>
        </choose>
    </select>

测试类:

    @Test
    public void queryData(){
        SqlSession session = MybatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        HashMap<String,Object> map = new HashMap<String, Object>();
        map.put("title","MySQL");
        map.put("author","大大");
        List<Blog> blogs = mapper.queryBlogChoose(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        session.close();
        //Blog(id=076fc171dbf242f380d4e471902f58bc, title=MySQL如此简单, author=欻欻, createTime=Tue Apr 07 10:32:24 CST 2020, views=100)
		//Blog(id=b779b5485e774e4186f4b543fc9e87dd, title=MySQL如此简单, author=丁丁, createTime=Tue Apr 07 11:22:03 CST 2020, views=100)
    }

可以看到,当title传入后,便不会按照传入的author进行模糊搜索。

3.trim、where、set

尽管上述问题已经得到解决,但是总不能实际开发也要为每个sql语句添加where 1=1,如果放到动态条件内又会出现错误,所以,需要使用到<where>标签。

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

所以,上题中的实现可以写做:

    <select id="queryBlogChoose" resultType="com.dlc.pojo.Blog">
        select * from blog
    <where>
        <choose>
            <when test="title!=null">
                and title like "%"#{title}"%"
            </when>
            <when test="author!=null">
                and author like "%"#{author}"%"
            </when>
        </choose>
    </where>
    </select>

用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

    <update id="updateBlog" parameterType="map">
        update blog
    <set>
        <if test="title!=null">
            title = #{title},
        </if>
        <if test="author!=null">
            author = #{author},
        </if>
    </set>
            where id = #{id}
    </update>

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

set的等价trim

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

4.sql片段

对于公用的(重复的)sql语句,可以抽取出来。

例如,使用<sql>标签抽取出片段:

    <sql id="if-query">
        <if test="title != null">
            and title like "%"#{title}"%"
        </if>
        <if test="author != null">
            and author like "%"#{author}"%"
        </if>
    </sql>

在其他地方通过<include>标签使用该片段

    <select id="queryBlogIF" resultType="com.dlc.pojo.Blog" parameterType="map">
        select * from blog
        <where>
            <include refid="if-query"/>
        </where>
    </select>

注意:

  • 基于单表来定义SQL片段
  • 不要存在where标签,影响复用

5.foreach

foreach 元素的功能非常强大,

  • 它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量
  • 它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值