mybaties--day01

1.原始JDBC操作存在的问题

1.1原始操作流程

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        //加载数据库驱动
        Class.forName("com.mysql.jdbc.Driver");
        //通过驱动管理类获取数据库链接
        connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
        //定义sql语句 ?表示占位符
        String sql = "select * from user where username = ?";
        //获取预处理statement
        preparedStatement = connection.prepareStatement(sql);
        //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
        preparedStatement.setString(1, "王五");
        //向数据库发出sql执行查询,查询出结果集
        resultSet = preparedStatement.executeQuery();
        //遍历查询结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getString("id") + "" + resultSet.getString("username"));
        }
        //释放资源
        if (resultSet != null) resultSet.close();
        if (preparedStatement != null) preparedStatement.close();
        if (connection != null) connection.close();
    }

1.2原生JDBC操作存在问题分析:

1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

2、 Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

3、 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。

4、 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。

2.mybaties

2.1mybaties是什么?

mybatis是一个持久层的框架,是apache下的顶级项目。
mxbatis 托管到goolecode下,再后来托管到github下(https://github.com/mybatis/mybatis-3/releases)。
mybatis 让程序将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要sql语句。
mybatis 可以将向preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射

2.2 Mybatis架构

1、 mybatis配置

SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。

mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。

2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂

3、 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。

4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。

5、 Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sqlid即是Mapped statementid

6、 Mapped Statementsql执行输入参数进行定义,包括HashMap、基本类型、pojoExecutor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。

7、 Mapped Statementsql执行输出结果进行定义,包括HashMap、基本类型、pojoExecutor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。

 2.3工程搭建:

创建java工程

所需依赖:

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
<!--      <scope>test</scope>-->
    </dependency>
    <!--  spring-context:导入spring核心jar包  -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.2.RELEASE</version>
      <!--      <scope>test</scope>-->
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.46</version>
    </dependency>
    <!--    导入日志jar包-->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>jakarta.annotation</groupId>
      <artifactId>jakarta.annotation-api</artifactId>
      <version>1.3.4</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.0.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.0.2</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.5</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.5</version>
    </dependency>
    <!--        动态代理jar包-->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2.2</version>
    </dependency>
    <dependency>
      <groupId>asm</groupId>
      <artifactId>asm</artifactId>
      <version>3.3.1</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.2</version>
    </dependency>
    <!--        jdbcjar包-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.0.7.RELEASE</version>
    </dependency>
    <!--        事务支持jar包-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.0.7.RELEASE</version>
    </dependency>
    <!--        数据源jar包-->
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>
    <!--        另外一种数据源jar包commons-dbcp和commons-pool;-->
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>commons-pool</groupId>
      <artifactId>commons-pool</artifactId>
      <version>1.6</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.3.0</version>
    </dependency>
<!--    mybaties依赖javassist-->
    <dependency>
      <groupId>org.javassist</groupId>
      <artifactId>javassist</artifactId>
      <version>3.20.0-GA</version>
    </dependency>

编写log4j.properties

classpath下创建log4j.properties如下:

 

并将如下内容粘贴进去

# Global logging configuration
#开发环境设置成DEBUG,生产环境设置成INFO或者ERROR
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

mybatis默认使用log4j作为输出日志信息。

 上面的内容,在我们下载mybaties的时候,里面的帮助文档里面也可以找到:

创建SqlMapConfig.xml(名字不是SqlMapConfig也可以)

classpath下创建SqlMapConfig.xml,如下:(头文件也可以在官方文档找到)

<?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>
    <!-- 和spring整合后 environments配置将废除-->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事务管理-->
            <transactionManager type="JDBC"/>
            <!-- 数据库连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="admin123"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

SqlMapConfig.xmlmybatis核心配置文件,上边文件的配置内容为数据源、事务管理。

3.第一个查询例子:

需求:根据ID查询用户信息

写一个User.java类:

public class User {
    private int id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址
...get
...set
...toString

数据库创建对应的表

注意字段名要一一对应!!!并插入一条数据哦哦...

创建sql映射文件

classpath下的sqlmap目录下创建User类的sql映射文件usersMapper.xml:(头文件也可以在官方文档找到)

<?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命名空间,用于隔离sql语句,后面会讲另一层非常重要的作用。-->
<mapper namespace="test">
    <!--在映射文件中配置很多sql语句-->
    <!--需求;通过id查询用户表的记录-->
    <!--通过select执行数据库查询
    id:标识映射文件中的sql将sql语句封装到mappedStatement对象中,所以将id称为statement的id
    parameterType:指定输入参数的类型,这里指定int型
    #{}表示一个占位符号
    #{id}:其中的id表示接收输入的参数,参数名称就是id,如果输入参数是简单类型,#{}中的参数名可以任意,可以value或其它名称(因为在运行过程中mybaties最终执行的sql的时候#{***}会被换成?)
    resultType:指定sql输出结果的所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象。
-->
<select id="findUserById" parameterType="int" resultType="mybaties.pojo.User">
    SELECT * FROM USER WHERE ID=#{id}
</select>
</mapper>

加载映射文件

mybatis框架需要加载映射文件,将usersMapper.xml添加在SqlMapConfig.xml,如下:

 

<?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>
 ...
<!--    加载映射文件-->
    <mappers>
        <mapper resource="sqlmap/userMapper.xml"></mapper>
    </mappers>
</configuration>

测试程序:

import mybaties.pojo.User;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;

public class _01Mybatis_first {
    @Test
    public void test() {

//    1.配置文件
        String mapper = "SqlMapConfig.xml";
        SqlSession sqlSession = null;

//    2.将配置文件加载到流
        try {
            InputStream stream = Resources.getResourceAsStream(mapper);
//     3.创建会话工厂,传入mybaties配置文件
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(stream);
//     4.通过工厂得到sqlSession
            sqlSession = sessionFactory.openSession();
//          通过Sq1Session操作数据库
//              第一个参数:映射文件中statement的id,等于=namespace+"."+statement的id
//              第二个参数:指定和映射文件中所匹配的parameterType类型的参数
//           sqlSession.selectOne()返回的结果就是映射文件中所匹配的resultType类型的对象
            User user = sqlSession.selectOne("test.findUserById", 1);
            System.out.println(user);
//
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
//            释放资源
            sqlSession.close();
        }
    }
}

4.根据用户名模糊查询用户数据:

userMapper.xml添加如下内容:

    <!--根据用户名称模糊查询用户信息,可能返回多条
    resultType:指定就是单条记录所映射的java对象类型
    ${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sq1中。
    使用${}拼接sql,引起sql注入漏洞
    ${value}:接收输入参教的内容,如果传入类型是简单类型,${}中只能使用value
    -->
<select id="findUserByName" resultType="mybaties.pojo.User" parameterType="java.lang.String">
    SELECT *FROM USER WHERE USERNAME LIKE '%${value}%'
</select>

测试代码:

    @Test
    public void testByName() {
        String mapper = "SqlMapConfig.xml";
        SqlSession sqlSession = null;
        try {
            InputStream stream = Resources.getResourceAsStream(mapper);
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(stream);
            sqlSession = sessionFactory.openSession();
//            List<User> list = sqlSession.selectList("test.findUserByName", "沁");
            List<User> list = sqlSession.selectList("test.findUserByName", "' OR 1=1#");
            //上面这行代码演示的是注入漏洞,最终会查询出user表所有的数据;
            // (sql语法:#后面的内容就是注释内容)
//            所以,最终执行的SQL语句是:SELECT *FROM USER WHERE USERNAME LIKE '%' OR 1=1
            System.out.println(list);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
    }

5.插入用户信息:

userMapper.xml添加如下内容:

 <!--添加用户
    parameterType:指定输入参数类型是pojo(包括用户信息)
    #{}中指定poio的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值
-->
    <insert id="insertUser" parameterType="mybaties.pojo.User">
        INSERT INTO USER (USERNAME,BIRTHDAY,SEX,ADDRESS) VALUES (#{username},#{birthday},#{sex},#{address})
    </insert>

测试代码:

//    插入用户信息
    @Test
public void insertUser(){
        SqlSession session = getSession();//调用自己封装的统一获取sqlSession的方法
        User user = new User();
        user.setUsername("刘悦");
        user.setSex("1");
        user.setBirthday(new Date());
        user.setAddress("美国");
        session.insert("test.insertUser",user);
        session.commit();
        session.close();
    }

6.插入用户信息并返回插入的最新数据的主键:

获取自增主键:

 userMapper.xml添加如下内容:

<!--    添加用户,并返回刚刚插入数据对应的自增主键-->
    <insert id="insertUserAndGetKey" parameterType="mybaties.pojo.User">
        <!--
        将插入数据的主键返回,返回到user对象中
        SELECT LAST_INSERT_ID():得到刚insert进去记录的主键值,只适用与自增主键
        keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
        order:SELECT LAST_INSERT_ID()的执行顺序,相对于insert语句来说它的执行顺序
        resultType:指定SELECT LAST_INSERT_ID()的结果类型
        -->
        <selectKey keyProperty="id" resultType="java.lang.Integer" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT INTO USER (USERNAME,BIRTHDAY,SEX,ADDRESS) VALUES (#{username},#{birthday},#{sex},#{address})
    </insert>

演示代码:

//    插入用户信息并获取新插入数据的自增主键
    @Test
public void insertUserAndGetKey(){
        SqlSession session = getSession();//调用自己封装的统一获取sqlSession的方法
        User user = new User();
        user.setUsername("刘悦");
        user.setSex("1");
        user.setBirthday(new Date());
        user.setAddress("美国");
        session.insert("test.insertUserAndGetKey",user);
        System.out.println("主键:"+user.getId());
        session.commit();
        session.close();
    }

获取非自增主键:

  userMapper.xml添加如下内容:

<!--    添加用户,并返回刚刚插入数据对应的非增主键-->
    <insert id="insertUserAndGetKey2" parameterType="mybaties.pojo.User">
        <!--
            使用mysql的uuid()生成主键执行过程:
            首先通过uuid()得到主键,将主键设置到user对象的id属性中
            其次在insert执行时,从user对象中取出id属性值
        -->
        <selectKey keyProperty="id" resultType="java.lang.Integer" order="BEFORE">
            SELECT UUID()
        </selectKey>
        INSERT INTO USER (ID,USERNAME,BIRTHDAY,SEX,ADDRESS) VALUES (#{id},#{username},#{birthday},#{sex},#{address})
    </insert>

补充:获取ORCAL序列主键

userMapper.xml添加如下内容:

<!--    添加用户,并返回刚刚插入数据对应的非增主键-->
    <insert id="insertUserAndGetKey2" parameterType="mybaties.pojo.User">
       
        <selectKey keyProperty="id" resultType="java.lang.Integer" order="BEFORE">
            SELECT 序列名.nextval()
        </selectKey>
        INSERT INTO USER (ID,USERNAME,BIRTHDAY,SEX,ADDRESS) VALUES (#{id},#{username},#{birthday},#{sex},#{address})
    </insert>

 7.删除一条数据

userMapper.xml添加如下内容:

<!--删除用户-->
<!--    根据ID删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
    DELETE FROM USER WHERE ID=#{id}
</delete>

测试代码:

//    删除用户信息
    @Test
public void deleteUser(){
        SqlSession session = getSession();//调用自己封装的统一获取sqlSession的方法
        session.delete("test.deleteUser",6);
        session.commit();
        session.close();
    }

8.更新数据

userMapper.xml添加如下内容:

<!--
根据id更新用户
分析:
需要传入用户的id
需要传入用户的更新信息
parameterType指定user对象,包括id和更新信息,注意:id必须存在
#{id}:从输入user对象中获取id属性值
-->
<update id="updateUser" parameterType="mybaties.pojo.User">
    UPDATE USER SET username= #{username},birthday=#{birthday},sex=#{sex},address=#{address}  WHERE ID=#{id}
</update>

测试程序:

//    更新用户信息
    @Test
public void updateUser(){
        SqlSession session = getSession();//调用自己封装的统一获取sqlSession的方法
        User user = new User();
        user.setId(5);
        user.setUsername("刘悦后");
        user.setSex("1");
        user.setBirthday(new Date());
        user.setAddress("加拿大");
        session.update("test.updateUser",user);
        System.out.println("主键:"+user.getId());
        session.commit();
        session.close();
    }

9.mybatis和hibernate本质区别和应用场景

hibernate:是一个标准ORM框架(对象关系映射)。入门门槛较高的,不需要程序写sql,sql语句自动生成了。
对sql语句进行优化、修改比较困难的。
应用场景:
适用与需求变化不多的中小型项目,比如:后台管理系统,erp、orm、oa。。
mybatis:专注是sgl本身,需要程序员自己编写sgl语句,sgl修改、优化比较方便。mybatis是一个不完全的ORM框架,虽然程序员自己写sql,mybatis 也可以实现映射(输入映射、输出映射)。
应用场景:
适用与需求变化较多的项目,比如:互联网项目。
企业进行技术选型,以低成本高回报作为技术选型的原则,根据项目组的技术力量进行选择。

10.总结原始 dao开发问题

1、dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
2、调用sglsesion.方法时将statement的id硬编码了

3、调用sglsession方法时传入的变量,由于sqlsession.方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。

11Mapper动态代理方式

11.1 开发规范

Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

Mapper接口开发需要遵循以下规范:

1、 Mapper.xml文件中的namespacemapper接口的类路径相同。

2、  Mapper接口方法名和Mapper.xml中定义的每个statementid相同

3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql parameterType的类型相同

4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sqlresultType的类型相同

userMapper.xml代码:

<?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">
<!--Mapper.xml文件中的namespace与mapper接口的类路径相同。-->
<mapper namespace="mybaties.mapper.UserMapper">
    <select id="findUserById" parameterType="int" resultType="mybaties.pojo.User">
    SELECT * FROM USER WHERE ID=#{id}
    </select>
</mapper>

UserMapper.java:

public interface UserMapper {
    /**
     * 2、  接口方法名和Mapper.xml中定义的每个statement的id相同
     * 3、 接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
     * 4、 接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
     */
    public User findUserById(int id) throws Exception;
}

selectOneselectList

动态代理对象调用sqlSession.selectOne()sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。

mapper接口方法参数只能有一个是否影响系统开发

系统框架中,dao层的代码是被业务层公用的。
即使mapper接口只有一个参数,可以使用包装类型的poio满足不同的业务方法的需求。
注意:持久层方法的参数可以包装类型、map...,service方法中建议不要使用包装类型(不利于业务层的可扩展,例如你的service方法参数是map别人调用service对应的方法时根本不知道你map里面有多少个key,或者key都是什么)

11SqlMapConfig.xml配置文件

11.1配置内容

Mybaties全局配置文件:SqlMapConfig.xml中配置的内容和顺序如下:

properties(属性)

settings(全局配置参数)

typeAliases(类型别名)

typeHandlers(类型处理器)

objectFactory(对象工厂)

plugins(插件)

environments(环境集合属性对象)

environment(环境子属性对象)

transactionManager(事务管理)

dataSource(数据源)

mappers(映射器)

11.2 properties(属性)重点

需求:

将数据库连接参数单独配置在db.propeties.中,只需要在SqlMapconfig.xml中加载db.properties.的属性值。

在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。

将数据库连接参数只配置在db.eroperties.中,原因:方便对参数进行统一管理,其它xml 可以引用该db.properties。

SqlMapconfig.xml

<?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标签-->
    <properties resource="db.properties">
<!--注意!!!properties标签内部还可以配置一下标签和属性-->
<!--        <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>-->
    </properties>
    <!-- 和spring整合后 environments配置将废除-->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事务管理-->
            <transactionManager type="JDBC"/>
            <!-- 数据库连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

<!--    加载映射文件-->
    <mappers>
        <mapper resource="sqlmap/userMapper.xml"></mapper>
        <mapper resource="mapper/userMapper.xml"></mapper>
    </mappers>
</configuration>

properties 特性:

注意:MyBatis 将按照下面的顺序来加载属性:

◆在properties元素体内定义的属性首先被读取。

然后会读取properties 元素中resource或url加载的属性,它会覆盖已读取的同名属性。

建议:

不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。

在properties 文件中定义属性名要有一定的特殊性,如:xxxxx.xxxxx.xxxx

11.3settings(全局配置参数):

mybais框架在运行时可以调整一些运行参数。

比如:开启二级缓存、开启延迟加载。。

全局参数将会影响mybatis.的运行行为。

详细参见https://blog.csdn.net/ycxzuoxin/article/details/104843730

<?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>
...
    <settings>
        <setting name="" value=""/>
    </settings>
...
</configuration>

11.4typeAliases(类型别名)重点

需求:

在mapper.xml中,定义很多的statement,statement 需要parameterType.指定输入参数的类型、需要resultType.指定输出结果的映射类型。

如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType.指定的类型定义一些别名,在mappe.xml中通过别名定义,方便开发。

mybatis支持别名:

别名

映射的类型

_byte

byte

_long

long

_short

short

_int

int

_integer

int

_double

double

_float

float

_boolean

boolean

string

String

byte

Byte

long

Long

short

Short

int

Integer

integer

Integer

double

Double

float

Float

boolean

Boolean

date

Date

decimal

BigDecimal

bigdecimal

BigDecimal

map

Map

自定义别名

<?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>
...
<!--    别名-->
    <typeAliases>
        <!--
        针对单个别名定义
        type:类型的路径
        alias:别名
        -->
        <typeAlias type="mybaties.pojo.User" alias="user"/>
        <!--
        批量别名定义
        指定包名,mybatis自动扫描包中的po类,自动定义别名,别名就是类名(首字母大写或小写都可以)
        -->
        <package name="mybaties.pojo"/>
    </typeAliases>

...
</configuration>

使用别名:

    <select id="findUserById" parameterType="int" resultType="user">
    SELECT * FROM USER WHERE ID=#{id}
    </select>

11.4mappers(映射器)重点

Mapper配置的几种方法:

<mapper resource="" />

使用相对于类路径的资源

如:<mapper resource="sqlmap/User.xml" />

<mapper class="" />

使用mapper接口类路径

如:<mapper class="mybatis.mapper.UserMapper"/>

注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

 <package name=""/>

注册指定包下的所有mapper接口

如:<package name="mybatis.mapper"/>

注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

 

!--    加载映射文件-->
    <mappers>
        <!--方法1:通过resource方法一次加载一个映射文件-->
<!--        <mapper resource="sqlmap/userMapper.xml"></mapper>-->
        <mapper resource="mapper/UserMapper.xml"></mapper>

        <!--方法2:通过mapper接口加载单个映射文件
         遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中
         上边规范的前提是:使用的是mapper代理方法
         -->
<!--        <mapper class="mybaties.mapper.UserMapper"/>-->


        <!--方法3:批里加载mapper(推荐使用)
        指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载
        遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中
        上边规范的前提是:使用的是mapper代理方法-->
<!--        <package name="mybaties.mapper"/>-->
    </mappers>

12 输入映射和输出映射

 Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心。

parameterType(输入类型)

传递简单类型

参考上面内容。

传递pojo对象

Mybatis使用ognl表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称。

传递pojo包装对象

开发中通过pojo传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。

Pojo类中包含pojo。

需求:根据用户名查询用户信息,查询条件放到QueryVo的user属性中。

Uesr类扩展类:

/**
 * 用户扩展类
 * 假如有一天,我们项目已经上线,但是我们想扩展一下User类,而我们有不能轻易改动User,我们可以通过实现类的方式对她进行扩展
 */
public class UserExtension extends User {


}

User类包装类:

/**
 * 用户包装类
 */
public class UserQueryVo {

    //    在这里包装所有需要的查询条件:下面只包装了user对象(子类)
    private UserExtension userExtension;

    //      ....还可以包装其他查询条件,如,订单商品,等
    public UserExtension getUserExtension() {
        return userExtension;
    }

    public void setUserExtension(UserExtension userExtension) {
        this.userExtension = userExtension;
    }
}

mapper.xml新增statement:

<!--
        综合查询,复杂查询条件
        #{userExtension.sex}:取出用户包装类中的性别值
        ${userExtension.username}:取出用户包装类中的用户名称
        parameterType="mybaties.vo.USerQueryVo":传递进来的复杂参数都包含在USerQueryVo对象中
        resultType="mybaties.extension.UserExtension":返回值都被添加到扩展过的User子类UserExtension中(因为查询出来的内容可能包括User属性之外的内容)
-->
    <select id="findUserList" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension">
        SELECT * FROM USER WHERE SEX=#{userExtension.sex} AND USERNAME LIKE'%${userExtension.username}%'
    </select>

mapper.java新增查询方法:

  public List<UserExtension> findUserList(UserQueryVo queryVo) throws Exception;

测试代码:

    @Test
    public void findUserList() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        mybaties自动生成mapper代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//        创建用户类扩展类并赋值查询条件
        UserExtension user = new UserExtension();
        user.setSex("1");
        user.setUsername("刘悦");
//        创建用户类包装类,并给userExtension属性赋值
        UserQueryVo queryVo = new UserQueryVo();
        queryVo.setUserExtension(user);
        List<UserExtension> userList = mapper.findUserList(queryVo);
        System.out.println(userList);
    }

传递HashMap

<!--
传递HashMap参数
sex,username都是hashmap里面的key
-->
    <select id="findUserListByMap" parameterType="hashMap" resultType="mybaties.extension.UserExtension">
        SELECT * FROM USER WHERE SEX=#{sex} AND USERNAME LIKE'%${username}%'
    </select>

resultType(输出类型)

使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
如果查询出来的列名和pojo中的属性名全部不一致,没有创建 poio对象。

只要查询出来的列名和pojo中的属性有一个一致,就会创建 pojo对象。

 输出简单类型

<!--简单类型查询-->
<select id="findUserListCount" resultType="int" parameterType="mybaties.vo.UserQueryVo">
    SELECT  COUNT(*)  FROM USER WHERE SEX=#{userExtension.sex} AND USERNAME LIKE'%${userExtension.username}%'
</select>

 

输出简单类型必须查询出来的结果集有一条记录,最终将第一个字段的值转换为输出类型。

输出pojo对象

参考上面内容

 输出pojo列表

参考上面内容

resultMap

resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。

如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系,resultMap实质上还需要将查询结果映射到pojo对象中。

resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojolist实现一对一查询和一对多查询。

 mapper.xml:

<!--    resoutMap-->
    <!--定义resultMap
    将SELECT id id_,username username_FROM USER 和User类中的属性作一个映射关系
    type:resultMap最终映射的java对象类型,可以使用别名
    id:resultMap的唯一标识-->
    <resultMap id="userResultMap" type="mybaties.pojo.User">
    <!--
    <id />表示查询结果集中唯一标识,非常重要。如果是多个字段为复合唯一约束则定义多个<id />
        column:查询出来的列名
        property:type指定的pojo类型中的属性名最终resultMap对column和property作一个映射关系(对应关系)
    -->
        <id column="id_" property="id"></id>
    <!--
    <result>:对普通名映射定义
        column:查询出来的列名
        property:type指定的pojo类型中的属性名最终resultMap对column和property作一个映射关系(对应关系)
    -->
        <result column="username_" property="username"></result>
    </resultMap>
    <!--
    使用resultMap进行输出映射
    resultMap:指定定义的resultMap的id,如果这个resultMap在其它的mapper文件,(userResultMap)前边需要加namespace的值
    -->
    <select id="findUserByIdResultMap" parameterType="int"   resultMap="userResultMap">
    SELECT id id_,username username FROM USER WHERE ID=#{id}
    </select>

 

mapper.java:

    public User findUserByIdResultMap(int id) throws Exception;

测试:

    @Test
    public void findUserByIdResultMap() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.findUserByIdResultMap(5);
        System.out.println(user);
    } 

13 动态sql:

通过mybatis提供的各种标签方法实现动态拼接sql

if

需求根据查询条件是否传入,动态改变SQL,以改造下面的SQL为例:

    <select id="findUserList" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension">
        SELECT * FROM USER WHERE SEX=#{userExtension.sex} AND USERNAME LIKE'%${userExtension.username}%'
    </select>
<!--上面的statement改为动态SQL-->
    <select id="findUserListWhitDynamicSql" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension">
         SELECT * FROM USER
<!--where标签会去掉第一个查询条件前面的and-->
         <where>
         <if test="userExtension!=null">
             <if test="userExtension.sex!=null and userExtension.sex!=''">
                 and SEX=#{userExtension.sex}
             </if>
             <if test="userExtension.username!=null and userExtension.username!=''">
                 and USERNAME LIKE'%${userExtension.username}%'
             </if>
         </if>
         </where>
    </select>

测试:

//复杂参数综合查询(动态SQL)
    @Test
    public void findUserListWhitDynamicSql() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        mybaties自动生成mapper代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//        创建用户类扩展类并赋值查询条件
        UserExtension user = new UserExtension();

//        由于是动态sql,某个值如果不设置,对应的查询条件就不会拼接到SQL上
//        user.setSex("1");
//        user.setUsername("刘悦");
//        创建用户类包装类,并给userExtension属性赋值
        UserQueryVo queryVo = new UserQueryVo();
        queryVo.setUserExtension(user);
        List<UserExtension> userList = mapper.findUserListWhitDynamicSql(null);
        System.out.println(userList);
    }

Sql片段

Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的,如下:

 定义SQL片段:

    <!--    
    定义SQL片段
        id:SQL片段的唯一标识
        经验:是基于单表来定义SQL片段,这样话这个SQL片段可重用性才高
        在SQL片段中不要包括where
    -->
    <sql id="userWhere" >
        <if test="userExtension!=null">
            <if test="userExtension.sex!=null and userExtension.sex!=''">
                and SEX=#{userExtension.sex}
            </if>
            <if test="userExtension.username!=null and userExtension.username!=''">
                and USERNAME LIKE'%${userExtension.username}%'
            </if>
        </if>
    </sql>

引用SQL片段

<!--    引用Sql片段-->
    <select id="findUserListWhitDynamicSql" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension">
         SELECT * FROM USER
         <where>
        <!--如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace-->
            <include refid="userWhere"></include>
         </where>
    </select>

foreach

 需求:

在用户查询列表和查询总数的statement中增加多个id输入查询
sl语句如下:
两种方法:
SELECT * FROM USER WHERE id=1 OR id=3 OR id=5
SELECT*FROM USER WHERE id IN(1,3,5)

uesr包装类添加属性:

 mapper.xml:

    <sql id="userWhere" >
        <if test="userExtension!=null">
            <if test="userExtension.sex!=null and userExtension.sex!=''">
                and SEX=#{userExtension.sex}
            </if>
            <if test="userExtension.username!=null and userExtension.username!=''">
                and USERNAME LIKE'%${userExtension.username}%'
            </if>
        </if>
        <if test="arrayList!=null">
            <!--如果传入的存放id的list不为null,就使用foreach遍历该arrayList
            collection:指定输入对象中集合属性名
            item:每个追历生成对象中
            open:开始遍历时拼接的串
            close:结束遍历时拼擦的串
            separator:遍历的两个对象中需要拼接的串
            -->
            <!--
            最终实现下边的sql拼接:
            AND(id=1 OR id=10 OR id=16)
            -->
            <foreach collection="arrayList" item="list_each_id" open="AND(" separator="OR" close=")">
                ID=#{list_each_id}
            </foreach>
            <!--SELECT * FROM USER WHERE SEX=? AND ID IN( ? , ? , ? ) 拼接方法如下-->
<!--            <foreach collection="arrayList" item="list_each_id" open="AND ID IN(" separator="," close=")">-->
<!--                #{list_each_id}-->
<!--            </foreach>-->
        </if>
    </sql>
<!--  foreach:传入list参数测试-->
    <select id="findUserListWhitMuchId" parameterType="mybaties.vo.UserQueryVo" resultType="mybaties.extension.UserExtension">
         SELECT * FROM USER
<!--where标签会去掉第一个查询条件前面的and-->
         <where>
        <!--如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace-->
            <include refid="userWhere"></include>
         </where>
    </select>

测试:

//foreach(传入list参数测试)
    @Test
    public void findUserListWhitMuchId() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        UserExtension user = new UserExtension();
        user.setSex("1");
//        user.setUsername("刘悦");
        UserQueryVo queryVo = new UserQueryVo();
        ArrayList<Integer> integers = new ArrayList<>();
        integers.add(1);
        integers.add(3);
        integers.add(5);
        queryVo.setArrayList(integers);
        queryVo.setUserExtension(user);
        List<UserExtension> userList = mapper.findUserListWhitMuchId(queryVo);
        System.out.println(userList);
    }

 

 

 

 

 

 

 


 

原文地址:https://www.cnblogs.com/luzhanshi/p/13295199.html