Mybatis

1、框架


1.1、框架定义

框架(Framework) 是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种认为,框架是可被应用开发者定制的应用骨架、模板

简单的说,框架其实是软件的半成品,就是一组组件, 供你使用完成你自己的系统。它解决了软件过程当中的普遍性问题,从而简化了开发步骤,提高了开发的效率。

框架是安全的,可复用的,它是可以不断升级改造的软件。

框架是对某一个方面有用的,它不是全能的。

1.2、三层架构

mvc:web开发中,使用mvc架构模式。m:数据,v:视图,c:控制器

c控制器:接收请求,调用service对象,显示请求的处理结果,当前使用servlet作为控制器

v视图:使用jsp、html、css、js,显示请求的处理结果,把m中的数据显示出来

m数据:数据库mysql,文件、网络

mvc作用:

  • 实现解耦合
  • 让mvc各司其职
  • 使得系统扩展更好,更容易维护
  1. 界面层(视图层):接收用户的请求,调用service,显示请求的处理结果,包含了jsp、html、servlet等对象。对应的包controller
  2. 业务逻辑层:处理业务逻辑,使用算法处理数据的,把数据返回给界面层,对应的service包,和包中的xxxservice类,例(OrderService、Userservice)
  3. 持久层(数据访问层):访问数据库,或者读取文件,访问网络,获取数据,对应的包是dao。dao包中很多的类(OrderDao、UserDao)

1.3、三层架构请求的处理流程

用户发起请求—>界面层—>业务逻辑层---->持久层---->数据库(mysql)

为什么要使用三层架构?

  1. 结果清晰、耦合度第,各层分工明确
  2. 可维护高,可扩展性高
  3. 有利于标准化
  4. 开发人员可以只关注整个结构中的其中某一层的功能实现
  5. 有利于各层逻辑的复用

1.4、三层架构模式和框架

每一层对应着一个框架

  • 界面层—>SpringMvc框架
  • 业务逻辑层—>Spring框架
  • 持久层—>Mybatis框架

Mybatis框架:

MyBatis是一个优秀的基于java的持久层框架,内部封装了jdbc;开发者只需要关注sql语句本身,而不需要处理加载驱动、创建连接、创建statement、 关闭连接

Spring框架:

Spring框架为了解决软件开发的复杂性而创建的。Spring 使用的是基本的JavaBean来完成以前非常复杂的企业级开发。

SpringMVC框架:

Spring MVC属于SpringFrameWork 3.0版本加入的一个模块,为Spring 框架提供了构建Web应用程序的能力。

框架能解决的问题

  1. 框架能实现技术的整合
  2. 提供开发效率,减低难度

jdbc访问数据库的优缺点

优点:

  • 直观好理解

缺点:

  • 创建很多对象 Connection、Statement、RestultSet
  • 注册驱动
  • 执行sql语句
  • 把RestultSet作为Student,List集合
  • 关闭资源
  • sql语句和业务逻辑代码混在一起

2、MyBatis框架


2.1、什么是MyBatis?

Mybatis

  • Mybatis是apache的一个开源项目iBatis, 2010年这个项目由apacheoftware foundation 迁移到了google code, 并且改名为MyBatis 。2013年11月迁移到 Githut
  • MyBatis是一个优秀的持久层框架,支持自定义SQL,存储过程和高级映射
  • Mybatis对原有的JDBC操作进行了封装,几乎消除了所有JDBC代码,使开发者只需关注SQL本身
  • Mybatis可以使用简单的XML或Annotaion来配置执行sql,并自动完成ORM操作,将执行结果返回

ORM框架

  • ORM(Object Relational Mapping)对象关系映射,将程序中的一个对象与表中的一行数据一一对应
  • ORM框架提供了持久化与表的映射关系,在运行时参照映射文件的信息,将对象持久化到数据库中

Mybatis解决的主要问题

  • 减少使用JDBC的复杂性,不用编写重复的创建Connection、Statement;不用编写关闭资源的代码
  • 直接使用java对象,表示结果数据。让开发者专注于SQL的处理,其他分心的工作由Mybatis代劳

Mybatis能做什么?

  1. 注册驱动
  2. 创建jdbc中使用的Connection、Statement、Restult
  3. 执行sql语句,得到Restult
  4. 处理RestultSet,把记录中的数据转为java对象,同时还能把java对象放入list集合中
  5. 关闭资源
  6. 实现sql语句的和java代码的解耦合

访问与下载

3、Mybatis入门

思路:搭建环境—>导入Mybatis—>编写代码—>测试!

3.1、搭建环境

CREATE DATABASE IF NOT EXISTS `mybatis`;

USE mybatis;

CREATE TABLE `user`(
    `id` INT(20) NOT NULL,
    `name` VARCHAR(30) DEFAULT NOT NULL,
    `pwd` VARCHAR(30) DEFAULT NOT NULL,
    `gender` int(2) DEFAULT NOT NULL,
    PRIMARY KEY(id)
)ENGINE = INNODB DEFAULT CHARSET=UTF8;

INSERT INTO `user` VALUES (null,'李思','666666',1);
INSERT INTO `user` VALUES (null,'晓天','111111',0);
INSERT INTO `user` VALUES (null,'小林','888888',1);
INSERT INTO `user` VALUES (null,'李明','666666',0);
INSERT INTO `user` VALUES (null,'小米','777777',1);
  1. 新建一个普通的Maven项目

  2. 删除src目录(父工程)

  3. 导入maven依赖

    <!--导入依赖-->
    <dependencies>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <!--junit单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
    </dependencies>
    

3.2、创建一个模块

  • 编写mybatis的核心配置文件

    <?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>
        <!--导入连接外部参数-->
        <properties resource="db.properties"/>
        <!--设置-->
        <settings>
            <!--标准的日志工厂-->
            <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
            <setting name="logImpl" value="LOG4j"/>
        </settings>
        <!--实体类别名-->
        <typeAliases>
            <!--定义类的别名-->
            <!--<typeAlias type="com.zhang.pojo.User" alias="user"/>-->
            <!--定义实体类所在的package,每个实体都会自动注册一个别名 = 类名-->
            <!--自动扫描包,将原类名作为别名-->
            <package name="com.zhang.pojo"/>
        </typeAliases>
    
        <!--核心配置信息-->
        <environments default="development">
            <!--数据库相关配置-->
            <environment id="development">
                <!--事务控制类型-->
                <transactionManager type="JDBC"/>
                <!--配置数据源:创建Connection对象-->
                <dataSource type="POOLED">
                    <!--数据连接参数-->
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>
    
        <!--mapper注册-->
        <mappers>
            <!--注册mapper文件所在的位置:目的是找到其他文件的sql语句-->
            <!--
                注:resource = "mapper文件的路径,使用 / 分隔"
                一个mapper resource指定一个mapper文件
            -->
            <mapper resource="com/zhang/dao/UserDaoMapper.xml"/>
        </mappers>
    </configuration>
    
  • 编写mybatis工具类

    //sqlSessionFactory  --->sqlSession
    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
    
        static {
            try {
                //使用Mybatis第一步:获取sqlSessionFactory
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例
        //SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
        public static SqlSession getSqlSession() {
            return sqlSessionFactory.openSession();
        }
    }
    

3.3、编写代码

  • 实体类

    package com.zhang.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    
    /**
     * 和数据库中的字段做映射
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class User {
        private int id;
        private String userName;
        private String pwd;
        private int gender;
    }
    
  • Dao接口

    package com.zhang.dao;
    
    import com.zhang.pojo.User;
    
    import java.util.List;
    
    public interface UserDao {
        //查询所有用户
        List<User> selectAllUsers();
    }
    
  • 接口实现类由原来的UserDaoImpl转变为一个Mapper配置文件

    <?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">
    <!--namespace = 所需实现接口的全限定名-->
    <mapper namespace="com.zhang.dao.UserDao">
        <!--id = 所需重写的接口抽象方法名  resultType = 查询后所需返回的对象类型-->
        <select id="selectAllUsers" resultType="user">
            select id, id, username, pwd, gender
            from mybatis.users
        </select>
    </mapper>
    
    <!--
                1.约束文件
                "http://mybatis.org/dtd/mybatis-3-mapper.dtd"
                约束文件作用:定义和限制当前文件中可以使用的标签和属性,以及标签出现的顺序
                2.mapper是根标签
                namespace:命名空间,必须有值,不能为空,唯一值
                推荐使用Dao接口的全限定名称
                3.在mapper标签中可以写<insert>、<delete>、<update>、<select>等标签
            -->
    

3.4、测试

注:org.apache.ibatis.binding.BindingException: Type interface com.zhang.dao.UserDao is not known to the MapperRegistry.

解决方案:核心配置文件中注册mappers

  • junit测试

    package com.zhang;
    
    import com.zhang.dao.UserDao;
    import com.zhang.pojo.User;
    import com.zhang.utils.MybatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.List;
    
    public class TestUserDao {
        @Test
        public void testSelectUserById() {
            //第一步:获得sqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            //方式一:getMapper  推荐使用
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            List<User> users = mapper.selectAllUsers();
            for (User user : users) {
                System.out.println(user);
            }
    
            //方式二:需要转换
            //List<User> userList = sqlSession.selectList("com.zhang.dao.UserDao.selectAllUsers");
            //for (User user : userList) {
            //    System.out.println(user);
            //}
            //关闭sqlSession
            sqlSession.close();
        }
    }
    

注:Could not find resource com/zhang/dao/UserDaoMapper.xml

解决方案:由于maven约定大于配置,在pom.xml的文件中修改maven的默认规则

<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
    <!--更改maven编译规则-->
    <resources>
        <resource>
            <!--资源目录-->
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

4、CRUD

4.1、select

4.1.1、序号参数绑定

public interface UserDao {
    //根据用户id和姓名查询用户
    User selectUserByIdAndUserName(Integer id, String userName);        //原生参数绑定
}
<select id="selectUserByIdAndUserName" resultType="user">
    select id, username, pwd, gender
    from mybatis.users
    where id = #{arg0}
    and username = #{arg1}		<!--arg0、arg1、arg2、argn-->
</select>

<select id="selectUserByIdAndUserName" resultType="user">
    select id, username, pwd, gender
    from mybatis.users
    where id = #{param1}
    and username = #{param2}	<!--param1、param2、param3、param(n)-->
</select>

4.1.2、注解参数绑定(推荐)

import org.apache.ibatis.annotations.Param;     //引入注解

public interface UserDao {
    //根据用户id和密码查询用户
    User selectUserByIdAndPassword(@Param("id") Integer id, @Param("password") String password);        //使用Mybatis提供的@Param进行参数绑定
}
<select id="selectUserByIdAndPassword" resultType="user">
    select id, username, pwd, gender
    from mybatis.users
    where id = #{id}
    and pwd = #{password}     <!--使用注解值@param{pwd}-->
</select>

4.1.3、Map参数绑定

public interface UserDao {
    User selectUserByUserNameAndPassword(Map values);        //添加Map进行参数绑定
}

UserDao mapper = sqlSession.getMapper(UserDao.class);
Map values = new HashMap();        //测试类创建Map
values.put("myusername", "李思");      //自定义key,绑定参数
values.put("mypwd", "666666");
User user = mapper.selectUserByUserNameAndPassword(values);
<select id="selectUserByUserNameAndPassword" resultType="user">
    select id, username, pwd, gender
    from mybatis.users
    where username = #{myusername}
    and pwd = #{mypwd}          <!--通过key获取value-->
</select>

4.1.4、对象参数绑定

public interface UserDao {
    User selectUserByUserNameAndPassword1(User user);       //使用对象进行参数绑定
}
<select id="selectUserByUserNameAndPassword1" resultType="user">
    select id, username, pwd, gender
    from mybatis.users
    where username = #{userName}
    and pwd = #{pwd}          <!--#{userName}取User对象的userName属性值,#{pwd}同理-->
</select>

4.1.5、模糊查询

public interface UserDao {
    List<User> selectByUserNameLike(@Param("keyword") String userName);     //根据用户名模糊查询
}
<select id="selectByUserNameLike" resultType="user">
    select id, username, pwd, gender
    from mybatis.users
    where username like concat('%',#{keyword},'%')         <!--使用mysql  concat拼接 '%'-->
</select>

4.2、delete

<delete id="deleteUserById" parameterType="int">
    delete
    from mybatis.users
    where id = #{id}
</delete>

4.3、update

<update id="updateUserById" parameterType="user">
    update mybatis.users
    set username = #{userName},
        pwd      = #{pwd},
        gender   = #{gender}
    where id = #{id}        <!--方法参数为对象时,可直接使用#{属性名}获取-->
</update>

4.4、insert

<insert id="addUser" parameterType="user">
    insert into mybatis.users
    values (#{id}, #{userName}, #{pwd}, #{gender});
    insert into mybatis.users
    values (null, #{userName}, #{pwd}, #{gender});		<!--自动主键-->
</insert>

注:增、删、改需要提交事务!

4.5、主键回填

4.5.1、通过last-insert_id()查询主键

<insert id="addUser" parameterType="user">
    <!--主键回填,将新数据的ID,存入java对象和主键对应的属性中-->
    <selectKey order="AFTER" resultType="int" keyProperty="id">    <!--插入之后-->
        select last_insert_id()     <!--适用于整数类型自增主键-->
    </selectKey>
    insert into mybatis.users
    values (#{id}, #{userName}, #{pwd}, #{gender});
</insert>

4.5.2、通过uuid()查询主键

<insert id="addStudent" parameterType="student">
    <selectKey order="BEFORE" keyProperty="id" resultType="string">   <!--插入之前-->
        select replace(uuid(),'-','')       <!--适用于字符串类型主键-->
    </selectKey>
    insert into mybatis.student values (#{id},#{name},#{age})
</insert>

5、配置解析


5.1、核心配置文件

  • mybatis-config.xml

  • Mybatis配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。

    configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
    environment(环境变量)
    transactionManager(事务管理器)
    dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)

5.2、环境配置(environments)

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

Mybatis默认的事务管理器就是JDBC,连接池:POOLED

5.3、属性(properties)

可以通过properties属性来引用配置文件;

这些属性可以在外部配置且进行动态替换。既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.properties

编写一个配置文件

db.properties

#key=value
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
username = root
password = root

在核心配置文件中映入

<!--引入web配置文件-->
<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="pwd" value="root"/>
</properties>
  • 可以直接引入外部文件
  • 可以在其中增加一些属性配置
  • 如果两个文件有同一个字段,优先使用外部配置文件

Mybatis

5.4、类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字

  • 它仅用于 XML 配置,意在降低冗余的全限定类名书写

    <!--可以给实体类起别名-->
    <typeAliases>
        <typeAlias type="com.zhang.pojo.Users" alias="user"/>
    </typeAliases>
    

    也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

    扫描实体类的包,它的默认别名就为这个类的类名:首字母小写

    <!--可以给实体类起别名-->
    <typeAliases>
        <package name="com.zhang.pojo"/>
    </typeAliases>
    

在实体类比较少的时候,使用第一种方式

如果实体类十分多,推荐使用第二种

第一种可以DIY(自定义)别名,第二种则不行(可以通过注解添加别名

@Alias("user")
public class Users {
    private int id;
    //......
}

5.5、设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为;

Mybatis

Mybatis

5.6、其他配置

5.7、映射器(mappers)

MapperRegistry:注册绑定我们的mapper文件;

方式一:【推荐使用】

<!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
<mappers>
    <mapper resource="com/zhang/dao/UserMapper.xml"/>
</mappers>

方式二:使用class文件绑定注册

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

注:

  • UserMapper接口要和它的UserMapper.xml配置文件必须同名!并且必须在同一包下

方式三:使用扫描包进行注入绑定

<mappers>    
    <package name="com.zhang.dao"/>
</mappers>

注意点:使用同方式二一致!

6、日志


6.1、什么是日志?

用于记录系统中发生的各种事件。记录的位置常见的有:控制台、磁盘文件等

日志的级别

日志级别从低到高:

TRACE、DEBUG、 INFO、 WARN、 ERROR、FATAL

日志的作用

  • 通过日志观察、分析项目的运行情况(项目维护)
  • 通过日志分析用户的使用情况(大数据分析)
  • ……

Mybatis

  • SLF4J
  • LOG4J
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING (控制台输出)
  • NO_LOGGING

在Mybatis中具体使用那个日志实现,在设置中设定!

STDOUT_LOGGING标准日志输出

在mybatis核心配置文件中,配置日志!

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

Mybatis

6.2、Log4j+Commons-Logging

什么是log4j?

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
  1. 导入依赖

    项目中添加Log4j和Commons-Logging依赖

    <dependencies>
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>
    
  2. 基本使用

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.junit.Test;
    
    public class LogTest {
        //日志对象
        private Log log = LogFactory.getLog(LogTest.class);     //需要输出日志的类,可以创建一个log属性
    
        @Test
        public void testLog(){
            log.trace("hello trace~~");
            log.debug("hello debug~~");
            log.info("hello info~~");
            log.warn("hello warn~~");
            log.error("hello error~~");
            log.fatal("hello fatal~~");
        }
    }
    
  3. 配置信息

    定义配置文件log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd">

<log4j:configuration>

    <!-- 日志输出到控制台 -->
    <appender name="myConsole" class="org.apache.log4j.ConsoleAppender">
        <!-- 日志输出格式 -->
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%p][%-d{yyyy-MM-dd HH:mm:ss SSS}][%c]-[%p] %m%n"/>
        </layout>

        <!--过滤器设置输出的级别-->
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <!-- 设置日志输出的最小级别 -->
            <param name="levelMin" value="TRACE"/>
            <!-- 设置日志输出的最大级别 -->
            <param name="levelMax" value="FATAL"/>
        </filter>
    </appender>


    <!-- 输出日志到文件 -->
    <appender name="myFile" class="org.apache.log4j.FileAppender">
        <!-- 输出文件全路径名-->
        <param name="File" value="d:/log/fileAppender.log"/>
        <!--是否在已存在的文件追加写:默认时true,若为false则每次启动都会删除并重新新建文件-->
        <param name="Append" value="false"/>
        <param name="Threshold" value="INFO"/>
        <!--是否启用缓存,默认false-->
        <param name="BufferedIO" value="false"/>
        <!--缓存大小,依赖上一个参数(bufferedIO), 默认缓存大小8K  -->
        <param name="BufferSize" value="512"/>
        <!-- 日志输出格式 -->
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%p][%d{yyyy-MM-dd HH:mm:ss SSS}][%c]-[%m]%n"/>
        </layout>
    </appender>


    <!-- 输出日志到文件,当文件大小达到一定阈值时,自动备份 -->
    <!-- FileAppender子类 -->
    <appender name="rollingAppender" class="org.apache.log4j.RollingFileAppender">
        <!-- 日志文件全路径名 -->
        <param name="File" value="d:/log/rollingAppender.log"/>      <!--文件位置-->
        <!--是否在已存在的文件追加写:默认时true,若为false则每次启动都会删除并重新新建文件-->
        <param name="Append" value="true"/>
        <!-- 保存备份日志的最大个数,默认值是:1  -->
        <param name="MaxBackupIndex" value="10"/>
        <!-- 设置当日志文件达到此阈值的时候自动回滚,单位可以是KB,MB,GB,默认单位是KB,默认值是:10MB -->
        <param name="MaxFileSize" value="10KB"/>
        <!-- 设置日志输出的样式 -->`
        <layout class="org.apache.log4j.PatternLayout">
            <!-- 日志输出格式 -->
            <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%-5p] [method:%l]%n%m%n%n"/>
        </layout>
    </appender>


    <!-- 日志输出到文件,可以配置多久产生一个新的日志信息文件 -->
    <appender name="dailyRollingAppender" class="org.apache.log4j.DailyRollingFileAppender">
        <!-- 文件文件全路径名 -->
        <param name="File" value="d:/log/dailyRollingAppender.log"/>
        <!--是否追加-->
        <param name="Append" value="true"/>
        <!-- 设置日志备份频率,默认:为每天一个日志文件 -->
        <param name="DatePattern" value="'.'yyyy-MM-dd'.log'"/>

        <!--每分钟一个备份-->
        <!--<param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm'.log'" />-->
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%p][%d{HH:mm:ss SSS}][%c]-[%m]%n"/>
        </layout>
    </appender>


    <!--
        1. 指定logger的设置,additivity是否遵循缺省的继承机制
        2. 当additivity="false"时,root中的配置就失灵了,不遵循缺省的继承机制
        3. 代码中使用Logger.getLogger("logTest")获得此输出器,且不会使用根输出器
    -->
    <logger name="logTest" additivity="false">
        <level value="INFO"/>
        <appender-ref ref="dailyRollingAppender"/>
    </logger>


    <!-- 根logger的设置,若代码中未找到指定的logger,则会根据继承机制,使用根logger-->
    <root>
        <!--优先级设置:all < trace < debug < info < warn < error < fatal < off-->
        <priority value="all"/>
        <appender-ref ref="myConsole"/>
        <appender-ref ref="fileAppender"/>
        <appender-ref ref="rollingAppender"/>
        <appender-ref ref="dailyRollingAppender"/>
    </root>

</log4j:configuration>

log4j

7、Mybatis工具类


7.1、封装工具类

  • Resource:用于获得读取配置文件的I0对象,耗费资源,建议通过10-次性读取所有所需要的数据。
  • SlSessionFactory: SqlSession工 类,内存占用多,耗费资源,建议每个应用只创建一个对象。
  • SqlSession:相当于Connection, 可控制事务,应为线程私有,不被多线程共享。
  • 将获得连接、关闭连接、提交事务、回滚事务、获得接口实现类等方法进行封装。
package com.zhang.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * 1、加载配置
 * 2、构建SqlSessionFactory
 * 3、创建SqlSession
 * 4、事务的管理
 * 5、Mapper的获取
 */
public class MybatisUtils {
    //创建ThreadLocal绑定当前线程中的SqlSession对象
    private static final ThreadLocal<SqlSession> t1 = new ThreadLocal<SqlSession>();
    private static SqlSessionFactory sqlSessionFactory;

    static {        //加载配置信息,并构建sqlSessionFactory
        try {
            //1.加载配置文件
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //2.构建sqlSessionFactory
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //通过sqlSessionFactory创建SqlSession
    //SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
    public static SqlSession getSqlSession() {
        //线程唯一,全局不唯一
        SqlSession sqlSession = t1.get();
        if (sqlSession == null) {
            sqlSession = sqlSessionFactory.openSession();
            t1.set(sqlSession);
        }
        return sqlSession;
    }

    //事务提交
    public static void commit() {
        SqlSession sqlSession = getSqlSession();
        sqlSession.commit();
        closeSqlSession();
    }

    //事务回滚
    public static void rollback() {
        SqlSession sqlSession = getSqlSession();
        sqlSession.rollback();
        closeSqlSession();
    }

    //资源释放
    public static void closeSqlSession() {
        SqlSession sqlSession = t1.get();
        if (sqlSession != null) sqlSession.close();
    }

    //获取Mapper
    public static <T> T getMapper(Class<T> mapper) {
        SqlSession sqlSession = getSqlSession();
        return sqlSession.getMapper(mapper);
    }
}

编写测试类,可以看出,步骤简化很多了

package com.zhang;

import com.zhang.dao.StudentDao;
import com.zhang.pojo.Student;
import com.zhang.utils.MybatisUtils;
import org.junit.Test;

public class TestStudentDao {
    @Test
    public void testAddStudent() {
        StudentDao mapper = MybatisUtils.getMapper(StudentDao.class);
        Student s1 = new Student(null, "testUtil", "18");
        mapper.addStudent(s1);
        System.err.println(s1.getId());
        MybatisUtils.commit();
        MybatisUtils.closeSqlSession();
    }
}

8、使用注解开发


8.1、面向接口编程

  • 大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
  • 根本原因:解耦 ,可拓展、提高复用、分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
  • 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
  • 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

关于接口的理解

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

三个面向区别

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程) 不是一个问题,更多的体现就是对系统整体的架构

8.2、使用注解开发

通过在接口中直接添加Mybatis注解,完成CRUD

@Select("  select id, username, pwd, gender from mybatis.users")
List<User> getusers();

需要在核心配置文件中绑定接口!

<mappers>
    <!--<mapper resource="com/zhang/dao/UserDaoMapper.xml"/>-->
    <mapper class="com.zhang.dao.UserDao"/>			<!--class="接口全限定名"-->
</mappers>

注:接口注解定义完毕后,需将接口全限定名注册到mybatis-config.xml的< mappers >中

经验:注解模式属于硬编码到java文件中,失去了使用配置文件外部修改的优势,可结合需求选用。

本质:反射机制实现

底层:动态代理

8.2.1、查询

public interface UserDao {
    @Select("  select id, username, pwd, gender from mybatis.users")
    List<User> queryAllUser();

    @Select("select id, username, pwd, gender from mybatis.users where id = #{id}")
    User queryUserById(@Param("id") Integer id);
}

8.2.2、删除

public interface UserDao {
    @Delete("delete from mybatis.users where id = #{id}")
    Integer deleteUser(@Param("id") Integer id);
}

8.2.3、添加

public interface UserDao {
    @Options(useGeneratedKeys = true,keyProperty = "id")        //自增key,主键为id
    @Insert(" insert into mybatis.users values (null, #{username}, #{pwd}, #{gender})")
    Integer insertUser(User user);
}

8.2.4、修改

public interface UserDao {
    @Update("update mybatis.users set username=#{username},pwd=#{pwd},gender=#{gender} where id=#{id}")
    Integer updateUser(User user);
}

9、ORM映射


9.1、Mybatis自动ORM失效

MyBatis只能自动维护库表”列名“与”属性名“相同时的一-对应关系, 二者不同时,无法自动ORM。

Mybatis

9.2、方案一:列的别名

在SQL中使用as为查询字段添加列别名,以匹配属性名。

<mapper namespace="com.zhang.dao.PersonDao">
    <select id="selectAllPerson" resultType="person">
        select id, username as name, password as pwd, gender as sex
        from mybatis.person
    </select>
</mapper>

9.3、方案二:结果集映射(ResultMap-查询结果的封装规则)

通过映射,匹配列名与属性名

<mapper namespace="com.zhang.dao.PersonDao">    <!--使用resultMap标签定义更复杂的映射规则-->    <resultMap id="person_resultMap" type="person">        <!--关联主键与列名-->        <id column="id" property="id"></id>        <!--关联列名与属性-->        <result column="username" property="name"></result>        <result column="password" property="pwd"></result>        <result column="gender" property="sex"></result>    </resultMap>    <!--使用resultMap作为ORM映射-->    <select id="selectAllPerson" resultMap="person_resultMap">        select id, username, password, gender        from mybatis.person    </select></mapper>

10、Mybatis处理关联关系-多表连接


实体间的关系:关联关系(拥有has、属于belong)

  • OneToOne:一对一关系(Passenger(旅客)-- Passport(护照))
  • OneToMany:一对多关系 (Employee(员工) 一Department(部门))
  • ManyToMany:多对多关系(Student(学生) – Subject(科目))

Mybatis

10.1、OneToOne

环境搭建

create table t_passengers(    `id` int primary key auto_increment,    name varchar(50),    sex varchar(1),    birthday date)default charset = utf8;create table t_passports(    id int auto_increment primary key ,    nationality varchar(50),    expire date,    passenger_id int unique ,    foreign key (passenger_id) references t_passengers(id))default charset  = utf8;insert into t_passengers values (null,'张绍刚','男','2002-1-1');insert into t_passengers values (null,'李晓峰','男','2001-5-15');insert into t_passengers values (null,'林晓东','男','1999-6-15');insert into t_passports values (null,'中国','2035-1-1',1);insert into t_passports values (null,'美国','2050-1-1',2);insert into t_passports values (null,'英国','2040-1-1',3);
<mapper namespace="com.zhang.dao.PassengerDao">    <!--结果集映射(查询结果的封装规则)-->    <resultMap id="passenger_passport" type="passenger">        <id column="id" property="id"></id>        <result column="name" property="name"></result>        <result column="sex" property="sex"></result>        <result column="birthday" property="birthday"></result>        <!--关系表中数据的封装规则-->        <!--描述passport nationality expire-:映射规则-->        <association property="passport" javaType="passport">            <!--指定关系表的实体属性-->            <id column="passId" property="id"></id>            <result column="nationality" property="nationality"></result>            <result column="expire" property="expire"></result>        </association>    </resultMap>    <!--查询旅客及其护照信息-->    <!--多表连接查询-->                     <!--结果集映射(查询结果的封装规则)-->    <select id="queryPassengerById" resultMap="passenger_passport">        <!--别名,避免id冲突-->        select enger.id, name, sex, birthday, passenger_id passId, port.nationality, port.expire        from t_passengers as enger        inner join t_passports as port        on enger.id = port.passenger_id        where enger.id = #{id}    </select></mapper>

10.2、OneToMany

环境搭建

create table t_departments(    id int primary key auto_increment,    name varchar(50),    location varchar(100))default charset  = utf8;create table t_employees(    id int primary key auto_increment,    name varchar(50),    salary double,    dept_id int,    foreign key (dept_id) references t_departments(id))default charset = utf8;insert into t_departments values (null,'研发部','北京'),(null,'教学部','上海'),(null,'品牌部','西安');insert into t_employees values (null,'李晓明',10000,1),(null,'林小芳',6000,1),(null,'张立伟',8000,2),(null,'赵伟峰',8800,2),(null,'林晓鸥',12000,3),(null,'赵凯峰',11000,3)
<mapper namespace="com.zhang.dao.DepartmentDao">    <!--封装规则-->    <resultMap id="dept_emp" type="department">        <id column="id" property="id"></id>        <result column="name" property="name"></result>        <result column="location" property="location"></result>        <!--emp_id  emp_name  salary   employee-->        <!--关系表中数据的封装骨规则-->         <!--指定关系表的实体类型-->        <collection property="employee" ofType="Employee">            <id column="emp_id" property="id"></id>            <result column="emp_name" property="name"></result>            <result column="salary" property="salary"></result>        </collection>    </resultMap>    <!--多表连接查询-->                   <!--封装规则-->    <select id="queryDepartmentById" resultMap="dept_emp">        <!--别名,避免id冲突-->        select d.id, d.name, location, emp.id emp_id, emp.name emp_name, emp.salary        from t_departments d        inner join t_employees emp        on d.id = emp.dept_id        where dept_id = #{id}    </select></mapper>

10.3、ManyToMany

Mybatis

环境搭建

create table t_student(    id   int primary key auto_increment,    name varchar(50),    sex  varchar(1)) default charset = utf8;create table t_subject(    id    int primary key auto_increment,    name  varchar(50),    grade varchar(1)) default charset = utf8;create table t_stu_sub(    student_id int,    subject_id int,    foreign key (student_id) references t_student (id),    foreign key (subject_id) references t_subject (id),    primary key (student_id, subject_id)) default charset = utf8;insert into t_student values (null,'小张','男'),(null,'小林','女');insert into t_subject values (1001,'JavaSE','1'),(1002,'JavaWeb','2');insert into t_stu_sub values (1,1001),(1,1002),(2,1001),(2,1002);
<mapper namespace="com.zhang.dao.SubjectDao">    <!--映射查询只封装两表中的信息,可忽略关系表内容-->    <resultMap id="subject_student" type="subject">        <id column="sub_id" property="id"></id>        <result column="sub_name" property="name"></result>        <result column="grade" property="grade"></result>        <collection property="studentList" ofType="student">            <id column="stu_id" property="id"></id>            <result column="stu_name" property="name"></result>            <result column="sex" property="sex"></result>        </collection>    </resultMap>    <!--三表连接查询-->                   <!--封装规则-->    <select id="querySubjectById" resultMap="subject_student">        select subject_id sub_id,sub.name sub_name,grade,t_student.id stu_id,t_student.name stu_name,sex        from t_student inner join t_subject sub        on t_student.id = sub.id      <!--通过t_stu_sub建立两者之间的关系-->        join t_stu_sub        on t_student.id = t_stu_sub.student_id        where subject_id = #{id}    </select></mapper>

注:指定"多方"关系时(集合),使用

JavaType用来指定实体类中属性的类型

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

10.4、关系总结

一方,添加对象;多方,添加集合。

双方均可建立关系属性,建立关系属性后,对应的Mapper文件中需使用< ResultMap >完成多表映射。

持有对象关系属性,使用< association property=“dept” javaType="department >

持有集合关系属性,使用< collction property=“emps” ofType="employee >

11、动态SQL


MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果

11.1、 SQL

<mapper namespace="com.zhang.dao.UserDao">    <!--抽取通用的sql片段-->    <sql id="user_field">        select id, username, pwd, gender        from mybatis.users    </sql>    <select id="queryAllUser" resultType="user">        <!--通过ID引用sql片段-->        <include refid="user_field"></include>    </select></mapper>

11.2、where

<mapper namespace="com.zhang.dao.UserDao">    <select id="queryUsers" resultType="user">        <include refid="user_field"></include>        <!--where标签:        1.补充where关键字        2.where字句中以or、and开头,会将or、and去掉    -->        <where>            <if test="username!=null">username=#{username}</if>            <if test="gender!=null">and gender=#{gender}</if>        </where>    </select></mapper>

11.3、set

<mapper namespace="com.zhang.dao.UserDao">    <update id="updateUser" parameterType="user">        update mybatis.users        <!--            set标签:                1.补充set                2.自动将set字句的,去除            -->        <set>            <if test="username!=null">username=#{username},</if>            <if test="pwd!=pwd">pwd=#{pwd},</if>            <if test="gender!=gender">gender=#{gender},</if>        </set>        where id = #{id}    </update></mapper>

11.4、trim

代替where、set

<mapper namespace="com.zhang.dao.UserDao">    <select id="queryUsers" resultType="user">        <include refid="user_field"></include>        <!--            prefix="where" 补充where关键字            prefixOverrides  where字句以or或and开头,会被去除掉        -->        <trim prefix="where" prefixOverrides="or|and">		<!--添加where前缀,自动忽略前缀-->            <if test="username!=null">username=#{username}</if>            <if test="gender!=null">and gender=#{gender}</if>        </trim>    </select></mapper>
<mapper namespace="com.zhang.dao.UserDao">	 update mybatis.users        <!--             prefix="set" 补充set关键字             suffixOverrides  自动将set字句的,去除        -->        <trim prefix="set" suffixOverrides=",">		<!--添加set前缀,自动忽略后缀-->            <if test="username!=null">username=#{username},</if>            <if test="pwd!=pwd">pwd=#{pwd},</if>            <if test="gender!=gender">gender=#{gender},</if>        </trim>        where id = #{id}    </update></mapper>

11.5、foreach

<mapper namespace="com.zhang.dao.UserDao">    <!--批量删除-->    <delete id="deleteManyUser" parameterType="java.util.List">        <!--delete from mybatis.users where id in(x,x,x,x)-->        delete from mybatis.users where id in        <foreach collection="list" open="(" close=")" item="id6" separator=",">            #{id6}        </foreach>    </delete>    <!--批量添加-->    <insert id="insertManyUser" parameterType="java.util.List">        <!--insert into mybatis.users values (null,x,x,x),(null,x,x,x)-->        insert into mybatis.users values        <foreach collection="list" item="user6" separator=",">            (null,#{user6.username},#{user6.pwd},#{user6.gender})        </foreach>    </insert></mapper>
参数 描述 取值
collection 容器类型 list、array、map
open 起始符 (
close 结束符 )
separator 分隔符 ,
index 下标号 从0开始,依次递减
item 当前项 任意名称(循环中通过#{任意名称}表达式访问)

12、缓存(Cache)


内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。

Mybatis

12.1、一级缓存

SqlSession级别的缓存,同一个SqlSession的发起多次同构查询, 会将数据保存在一级缓存中。

注:无需任何配置,默认开启一级缓存

12.2、二级缓存

SqlSessionFactory级别的缓存, 同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中

注:在Sqlsession.commit()或者Sqlsession.close()之后生效

12.2.1、开启全局缓存

setting是Mybatis中极为重要的调整位置,他们会改变Mybatis的运行行为,其他详细配置可参考官方文档

<configuration>    <!--导入连接外部参数-->    <properties resource="db.properties"/>    <!--设置-->    <settings>   <!--注意书写位置-->        <setting name="cacheEnabled" value="true"/>     <!--mybatis-config.xml中开启全局缓存(默认开启)-->    </settings>    <typeAliases>        <package name="com.zhang.pojo"/>    </typeAliases></configuration>

12.2.2、指定Mapper缓存

<mapper namespace="com.zhang.dao.UserDao">    <!--二级缓存是默认开启的,但并不是所有的查询结果,都会进入二级缓存(相当于邀请函)-->    <cache/></mapper>

12.2.3、缓存清空并重新缓存

@Testpublic void testMapperCache(){    //通过相同的sqlSessionFactory获取多个SqlSession    SqlSession sqlSession1 = MybatisUtils.openSqlSession();    UserDao mapper1 = sqlSession1.getMapper(UserDao.class);    List<User> users1 = mapper1.queryAllUser();    sqlSession1.close();        //必须关闭sqlSession才可缓存数据    //修改  修改相关的数据,会被移除    SqlSession sqlSession4 = MybatisUtils.openSqlSession();    UserDao mapper4 = sqlSession4.getMapper(UserDao.class);    Integer i = mapper4.deleteUser(5010);    sqlSession4.commit();       //DML成功,数据发生变化,缓存清空    sqlSession4.close();    SqlSession sqlSession2 = MybatisUtils.openSqlSession();    UserDao mapper2 = sqlSession2.getMapper(UserDao.class);    System.out.println("***************");    List<User> users2 = mapper2.queryAllUser();    sqlSession2.close();        //缓存未击中,重新查询数据库、重新缓存    System.out.println("-----------------");    SqlSession sqlSession3 = MybatisUtils.openSqlSession();    UserDao mapper3 = sqlSession3.getMapper(UserDao.class);    List<User> users3 = mapper3.queryAllUser();    sqlSession2.close();}

13、Druid连接池


13.1、什么是Druid连接池?

Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请求凭证、统计SQL 信息、SQL 性能收集、SQL 注入检查、SQL 翻译等,程序员可以通过定制来实现自己需要的功能。

13.2、不同连接池对比

测试执行申请归还连接1000000(一百万)次总耗时性能对比

13.1.1、测试环境

环境 版本
OS OS X 10.8.2
CPU Intel i7 2GHz 4 Core
JVM Java Version 1.7.0_05

13.1.2、基准测试结果对比

Mybatis

  • Druid是性能最好的数据库连接池,tomcat-jdbc和druid性能接近。
  • Proxool在激烈并发时会抛异常,不适用。
  • C3P0和Prool都相当慢,影响sql执行效率。
  • BoneCP性能并不优越,采用LinkedTransterQueue并没有能够获得性能提升。
  • 除了bonecp, 其他的在JDK7. 上跑得比JDK6上快。
  • jboss datasource虽然稳定,但性能很糟糕。

13.3、配置pom.xml

在pom.xml引入Druid依赖

<!--druid连接池--><!-- https://mvnrepository.com/artifact/com.alibaba/druid --><dependency>    <groupId>com.alibaba</groupId>    <artifactId>druid</artifactId>    <version>1.1.22</version></dependency>

13.4、创建DruidDataSourceFactory

MyDruidDataSourceFactory并继承PooledDataSourceFactory,并替换数据源。

package com.zhang.utils;import com.alibaba.druid.pool.DruidDataSource;import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;/** * 连接池工厂 */public class MyDruidDataSourceFactory extends PooledDataSourceFactory {    public MyDruidDataSourceFactory() {        this.dataSource = new DruidDataSource();        //替换数据源    }}

13.5、修改mybatis-config.xml

mybatis-config.xml中连接池相关配置

<!--连接池--><dataSource type="com.zhang.utils.MyDruidDataSourceFactory">        <!--数据源工厂-->    <!--数据连接参数-->    <property name="driverClass" value="${driver}"/>    <property name="jdbcUrl" value="${url}"/>    <property name="username" value="${username}"/>    <property name="password" value="${password}"/></dataSource>

注:< property name=“属性名” />属性名必须com.alibaba.druid.pool.DruidAbstractDataSource中一致。

14、PageHelper


14.1、什么是PageHelper?

PageHelper是适用于MyBatis框架的一个分页插件,使用方式极为便捷,支持任何复杂的单表、多表分页查询操作。

14.2、访问与下载

官方网站:

下载地址:download

14.3、开发步骤

PageHelper中提供了多个分页操作的静态方法入口。

14.3.1、引入依赖

pom.xml中引入PageHelper依赖

<!--pageHelper分页--><!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --><dependency>    <groupId>com.github.pagehelper</groupId>    <artifactId>pagehelper</artifactId>    <version>5.2.0</version></dependency>

14.3.2、配置mybatis-config.xml

在mybatis-config.xml中添加

<plugins>    <!--com.github.pagehelper为pagehelper类所在包名-->    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin></plugins>

14.3.3、PageHelper应用方式

使用PageHelper提供的静态方法设置分页查询条件

//在查询前,设置分页,查询第一页,每页5条数据//PageHelper 对其之后的第一个功能,进行分页功能追加PageHelper.startPage(1,5);

14.4、PageInfo对象

14.4.1、PageInfo应用方式

使用PageInfo保存分页查询结果

@Testpublic void testPage(){    //在查询前,设置分页,查询第一页,每页5条数据    //PageHelper 对其之后的第一个功能,进行分页功能追加    PageHelper.startPage(1,5);    List<User> users = mapper.queryAllUser();    for (User user : users) {        System.out.println(user);    }    //将查询结果封装到pageInfo对象中    PageInfo<User> pageInfo = new PageInfo<User>(users);    System.out.println(pageInfo);    MybatisUtils.closeSqlSession();}

14.4.2、注意事项

  • 只有在PagetHelperstartPage0方法之后的第一个查询会有执行分页
  • 分页插件**不支持带有"for update"**的查询语句。
  • 分页插件不支持**“嵌套查询**",会导致结果集折叠。所以无法保证分页结果数量正确。。
public class PageInfo<T> extends PageSerializable<T> {
    //当前页
    private int pageNum;		
    //每页的数量
    private int pageSize;
    //当前页的数量
    private int size;
    //当前页面第一个元素在数据库中的行号
    private long startRow;
    //当前页面最后一个元素在数据库中的行号
    private long endRow;
    //总页数
    private int pages;
    //上一页
    private int prePage;
    //下一页
    private int nextPage;
    //是否是第一页
    private boolean isFirstPage;
    //是否是最后一页
    private boolean isLastPage;
    //是否有上一页
    private boolean hasPreviousPage;
    //是否有下一页
    private boolean hasNextPage;
    private int navigatePages;
    private int[] navigatepageNums;
    private int navigateFirstPage;
    private int navigateLastPage;
}

15、$符号的应用场景


${attribute}属于字符串拼接SQL,而非预编译占位符,会有注入攻击问题,不建议在常规SQL中使用,常用于可解决动态升降序问题

15.1、$符号参数绑定

public interface UserDao {
    List<User> queryUsersByUserName(@Param("userName") String userName);
    List<User> queryUsersByIdDesc(@Param("rule")String rule);       //desc  asc
}
<mapper namespace="com.zhang.dao.UserDao">
    <select id="queryUsersByUserName" resultType="user">
        select id, username, pwd, gender
        from mybatis.users
        where username = '${userName}'      <!--拼接userName,如果字符串类型需要用单引号:'${userName}'-->
    </select>

    <select id="queryUsersByIdDesc" resultType="user">
        select id, username, pwd, gender
        from mybatis.users
        order by id ${rule}         <!--拼接desc | asc-->
    </select>
</mapper>
public class TestUserDao {
    @Test
    public void testQueryUsersByIdDesc() {
        UserDao mapper = MybatisUtils.getMapper(UserDao.class);
        List<User> users = mapper.queryUsersByIdDesc("desc");       //调用时传入desc | asc
    }
}

15.2、$符号注入攻击

<mapper namespace="com.zhang.dao.UserDao">
    <select id="queryUsersByUserName" resultType="user">
        select id, username, pwd, gender
        from mybatis.users
        where username = '${userName}'
    </select>		<!--会存在注入攻击,比如传入参数是【String name = "xxx' or '1'='1"】-->
</mapper>
/**
 * #{}:占位符 可以规避sql注入风险
 * 原则:填充数据,要和列相关的位置才能使用
 * select * from users where id = ?
 * insert into users values (?,?,?)
 * update users set username = ?,pwd = ?
 * ${}:存在sql注入问题,随意拼接数据
 */
@Test
public void testInject() {
    UserDao mapper = MybatisUtils.getMapper(UserDao.class);
    List<User> users = mapper.queryUsersByUserName("xxx' or '1'='1");       //由于存在sql注入问题,导致查询结果把全部用户给显示出来
    for (User user : users) {
        System.out.println(user);
    }
}

Mybatis

上一篇:Mapreduce实例——倒排索引


下一篇:SaltStack-return与job管理