mybatis总概览(1)(概念,增删改查例子)

mybatis开发手册

补充:mybatis

MyBATIS原理第三篇: SqlSession下的四大对象之一——执行器(executor)

1.jdbc的回顾:

必须先添加数据库驱动包:oracle或者mysql

一个实例:

Public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            
            try {
                //加载数据库驱动
                Class.forName("com.mysql.jdbc.Driver");
                
                //通过驱动管理类获取数据库链接
                connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "mysql");
                //定义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"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                //释放资源
                if(resultSet!=null){
                    try {
                        resultSet.close();
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if(preparedStatement!=null){
                    try {
                        preparedStatement.close();
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if(connection!=null){
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

补充:

预编译的好处是:当执行重复的sql语句,数据库将执行已经缓存编译过的sql,可提高效率。
(sql:结构化查询语言(Structured Query Language))

存在的问题:

1、数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库性能。
设想:使用数据库连接池管理数据库连接。

2、将sql语句硬编码到java代码中,如果sql 语句修改,需要重新编译java代码,不利于系统维护。
设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。

3、向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。
设想:将sql语句及占位符号和参数全部配置在xml中。

4、从resutSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,,不利于系统维护。
设想:将查询的结果集,自动映射成java对象。

ps:简单来说:数据库的连接浪费;固定的sql语句;固定的sql参数;固定的结果集。

 2,mybatis是什么

mybatis是一个持久层的框架,是apache下的顶级项目。
mybatis托管到goolecode下,再后来托管到github下(https://github.com/mybatis/mybatis-3/releases)。

mybatis让程序将主要精力放在sql上通过mybatis提供的映射方式自由灵活生成(半自动化,大部分需要程序员编写sql满足需要sql语句mybatis可以将向 preparedStatement中的输入参数自动进行输入映射将查询结果集灵活映射成java对象。(输出映射)
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,
并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

对statement补充:

继承了 Statement 接口中所有方法的 PreparedStatement 接口都有自己的executeQuery、executeUpdate 和 execute 方法。
Statement 对象本身不包含 SQL语句,因而必须给 Statement.execute 方法提供 SQL 语句作为参数。 PreparedStatement 对象并 不将SQL 语句作为参数提供给这些方法,因为它们已经包含预编译 SQL 语句。 CallableStatement 对象继承这些方法的PreparedStatement 形式。
对于这些方法的 PreparedStatement 或 CallableStatement版本,使用查询参数将抛出 SQLException。
Statement、PreparedStatement(它从 Statement 继承而来)和CallableStatement(它从 PreparedStatement 继承而来)。
它们都专用于发送特定类型的 SQL 语句: Statement 对象用于执行不带参数的简单 SQL 语句PreparedStatement 对象用于执行带或不带 IN参数的预编译 SQL 语句
CallableStatement 对象用于执行对数据库已存储过程的调用。

(简单来说:Statement直接传递sql;PreparedStatement传递预编译的sql;CallableStatement执行存储过程。)

 3,框架组成:

 

sqlMapConfig:全局配置,包括数据源,事务,运行配置。映射配置。

sqlSessionFactory:session工厂

sqlSession:发出sql语句

Executor:执行sql操作

mapstatement:封装sql语句,输入输出对象的封装。

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对象,sql的id即是Mapped statement的id。
6、Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,
Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7、Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,
Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。

4,关于mybatis下载:

mybaits的代码由github.com管理,地址:https://github.com/mybatis/mybatis-3/releases

项目目录:

mybatis-3.2.7.jar----mybatis的核心包
lib----mybatis的依赖包(特别是对于其中日志包,需要设置日志级别,在配置中)
mybatis-3.2.7.pdf----mybatis使用手册

特别注意:使用还需要加入mysql的驱动包

对于整个项目的结构:

(ps:
1,关于配置目录的选择,最好有条理的创建。
2,关于所依赖的日志包,特别是配置文件的添加:log4j.properties,来设置日志级别,配置的内容可从mybatis的压缩包中的pdf文件介绍中获取或者源码的例子中。
)

(ps:开发时注意环境日志的级别配置)

 ps:日志解释:

通过根元素指定日志输出的级别、目的地: 
#  日志输出优先级: debug < info < warn < error 
log4j.rootLogger=info,console 

Log4j提供的layout有以下几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

%p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL, 
%t: 输出产生该日志事件的线程名 
[]  - :没有实际意义直接输出(没有加%前缀的都是直接输出)
%m: 输出代码中指定的消息,产生的日志具体信息 
%n: 输出一个回车换行符,Windows平台为”\r\n”,Unix平台为”\n”输出日志信息换行 

可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如: 
%5p:指定输出级别的名称,最小的宽度是5,在5以内文本向右对齐。
2)%-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,”-”号指定左对齐。 
3)%.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。 
4)%20.30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符,就从左边交远销出的字符截掉。 

几个注意点:

1.简单类型的占位符#{id}中的id和value都是可以的(非简单类型比如:pojo)

2.对于拼接字符串的占位符${value}中参数是简单类型中间必须是value。比如模糊查询 select * from s like "%${value}%"

    补充:对于拼接字符串特别注意的是需要考虑到可能出现的sql注入。

3.对于结果集是单条还是多条记录对于配置中的返回类型都是一样的(并不需要list),但sqlSession需要使用selectSession()(而不是select())

4.sqlSession的关闭;

5.对于mysql插入数据获取id,需要插入后马上使用select LAST_INSERT_ID() 函数。(并发的是否存在冲突)

 

5,创建一个mybatis的实例步骤:

首先运行环境配置,加入jar包,配置依赖的日志参数(上面有介绍)

5.1,查询(模糊/非模糊查询;单个/列表查询)

1.配置mybatis的运行环境,数据源、事务等。

在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事务管理--也就是mybatis在管理(jdbc已被封装在mybatis中)>
            <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="mysql" />
            </dataSource>
        </environment>
    </environments>
    
</configuration>

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

总结:

配置的意思:
environments:运行环境主标签,default;当前采用的哪个子环境(根据子环境的id)
  environment:子环境,通过id来区别多个子环境配置
    transactionManager:事务管理,type:来指明谁来管理事务
    dateSource:数据源,连接相关,type表示采用连接池(mybatis集成的连接池)
      property:连接参数配置:驱动,数据库,账号密码

2.po类:关系映射对象

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

3.映射文件:(这里的映射是sql语句和代码方法的映射,不像hibernate需要配置属性和字段映射)

在classpath下的sqlmap目录下创建sql映射文件Users.xml:

关于映射文件命名:User.xml(原始ibatis命名),mapper代理开发映射文件名称叫XXXMapper.xml,比如:UserMapper.xml、ItemsMapper.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 namespace="test">
</mapper>

根据id查询的映射文件:

(ps:
1,namespace常规开发中用来作为不同SQL语句隔离划分,还有种代理方式有特别的意义
2,id将被封装到mappedStatement对象中,作为一条sql语句的标示
3,输入参数占位符#{}中的名称最好和相关数据对应。
4,返回类型,可以自定义一个类(但是属性和返回字段名称需要对应)。
)
<!-- 根据id获取用户信息 -->
    <select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">
        select * from user where id = #{id}
    </select>
    <!-- 自定义条件查询用户列表 -->
    <select id="findUserByUsername" parameterType="java.lang.String" 
            resultType="cn.itcast.mybatis.po.User">
       select * from user where username like '%${value}%' 
    </select>
View Code

总结:

mapper: 主标签,namespace:来分类sql语句
  select :查询语句,id:代表sql语句的名称,parameterType:参数类型,resultType:结果集的类型。
          注意:两个类型,了解基本类型和对象类型如何设置。
使用#{}占位符 基本类型可以使用java类型(需要加包名)或jdbc类型,会自动转换成jdbc类型。 查询语句:主要是占位符的设置,特别是对象类型设置
ps:对于简单类型(基本和string)不管是参数类型和返回类型的值都可以直接用类型的缩写。
  比如:int(不用java.lang.Interger);string(不用java.lang.String))
)

 

4.在sqlMapConfig.xml中加载User.xml:

<mappers>
        <mapper resource="sqlmap/User.xml"/>
</mappers>

5.测试代码

public class Mybatis_first {
    
    //会话工厂
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void createSqlSessionFactory() throws IOException {
        // 配置文件
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);

        // 使用SqlSessionFactoryBuilder从xml配置文件中创建SqlSessionFactory
        sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(inputStream);

    }

    // 根据 id查询用户信息
    @Test
    public void testFindUserById() {
        // 数据库会话实例
        SqlSession sqlSession = null;
        try {
            // 创建数据库会话实例sqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 查询单个记录,根据用户id查询用户信息
            User user = sqlSession.selectOne("test.findUserById", 10);
            // 输出用户信息
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }

    }

    // 根据用户名称模糊查询用户信息
    @Test
    public void testFindUserByUsername() {
        // 数据库会话实例
        SqlSession sqlSession = null;
        try {
            // 创建数据库会话实例sqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 查询单个记录,根据用户id查询用户信息
            List<User> list = sqlSession.selectList("test.findUserByUsername", "张");
            System.out.println(list.size());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }

    }
}
View Code

上面折叠代码还包括了:模糊查询,单个结果集合和列表结果集。

有3个方面注意:

1.#{}和${}
  #{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入
#{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。 ${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换。(ps:只是表示传入的是没有引号的字符串的值)
${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value
ps:以上2种区别:
#{}里面的字段总是可以和属性名称相关。${}里面的字段分2种情况,根据传入的数据类型,简单类型必须是value,对象类型必须是想要的属性名称。
建议只使用#{}的形式,因为简单很多。 ${}还隐患那就是存在注入风险,而#{}不会(不采用拼接已有字符串,整体直接类型转换)
2.parameterType和resultType parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在sql中。 resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。 3.selectOne和selectList selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常: org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3 at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70) selectList可以查询一条或多条记录

PS:总结:实现上注意:

实现单个结果和结果列表区别:
具体方法上单个采用selectOne();列表采用selectList()。
实现模糊查询和非模糊查询区别:
非模糊采用占位符#{},参数是基本类型的话中括号里面字段可以是任意;
(#{},如果在代码的方法中传入参数是对象(并且配置的也是传入是类),那么{}中的字段代表了对象的一个属性,也就是#{属性字段}插入的只是对象的一个属性值
如果是基本类型或者String类型的参数,那么#{字段}的字段名字可以是任意情况。)

模糊采用${},基本类型中括号里面的字段只能是value标识;
${字段},中的字段名字(非value)总是代表了传入对象的一个属性字段,如果传入的参数是基本类型或者字符串类型那么${字段}的字段必须是value(即${value}
${}还有一个特性那就是传入的是字符串的值,是没有引号的,比如like '${}'其中占位符是必须被引号包裹的,才能组合成字符串,否则语句结构只是字符串的值。
比如下面:
<select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User">
       select * from user where username like '%${value}%' 
</select>
1,上面情况占位符必须${value}(把传入参数拼接到占位符),而不能是${username}(代表传入参数被当作一个对象,然后取其中的username属性值,去拼接)
并且${}表示字符串会和其他字符串拼接如果是用#{}的占位符,那么like %#{value}%是错误的,只能是like #{value}(即不能和%号拼接)
2,sql语句中like 后面的语句必须是字符串 ,${}形式必须是有引号(‘ ’)的形式;#{}形式不用。

如果参数是对象的某个属性,那么占位符都要是具体的属性名称;
parameterType参数类型要设置成属性所在的类(而不是属性的类型)。

注意:模糊查询sql注入的问题!(可以通过检查字符串的方式避免)

5.2,添加,插入数据

映射文件,在 User.xml中配置添加用户的Statement

测试代码:

获取和插入两种主键方式::

1.mysql自增主键返回
mysql自增主键,执行insert提交之前自动生成一个自增主键。
通过mysql函数获取到刚插入记录的自增主键:
LAST_INSERT_ID()

 

通过修改sql映射文件,可以将mysql自增主键返回:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
        <!-- selectKey将主键返回,需要再返回 -->
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            select LAST_INSERT_ID()
        </selectKey>
       insert into user(username,birthday,sex,address)
        values(#{username},#{birthday},#{sex},#{address});
    </insert>
View Code

添加selectKey实现将主键返回

keyProperty:返回的主键存储在pojo中的哪个属性
order:selectKey的执行顺序,是相对与insert语句来说,由于mysql的自增原理执行完insert语句之后才将主键生成,所以这里selectKey的执行顺序为after
resultType:返回的主键是什么类型
LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。

2. Mysql使用 uuid实现主键(order是before,因为uuid在插入之前生成的)

使用mysql的uuid()函数生成主键,需要修改表中id字段类型为string,长度设置成35位。

执行思路:

先通过uuid()查询到主键,将主键输入 到sql语句中。
执行uuid()语句顺序相对于insert语句之前执行。

<insert  id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey resultType="java.lang.String" order="BEFORE" 
keyProperty="id">
select uuid()
</selectKey>
insert into user(id,username,birthday,sex,address) 
         values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
注意这里使用的order是“BEFORE”
View Code

这种映射目的:不仅仅是为了得到查询结果,实际目的是数据库中生成自增主键,并且在代码对象中得到主键。

测试代码:

2.2oracle的序列生成主键

        <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
            SELECT 序列名.nextval()
        </selectKey>
        insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address})
首先自定义一个序列且用于生成主键,selectKey使用如下:
<insert  id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey resultType="java.lang.Integer" order="BEFORE" 
keyProperty="id">
SELECT 自定义序列.NEXTVAL FROM DUAL
</selectKey>
insert into user(id,username,birthday,sex,address) 
         values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
注意这里使用的order是“BEFORE”
View Code

插入主键总结:映射的关键标签selectKey ,定义主键名称,函数持续的顺序,主键的类型,主键函数。

 

5.3,删除

映射文件/测试:

<!-- 删除用户 -->
    <delete id="deleteUserById" parameterType="int">
        delete from user where id=#{id}
    </delete>
// 数据库会话实例
        SqlSession sqlSession = null;
        try {
            // 创建数据库会话实例sqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 删除用户
            sqlSession.delete("test.deleteUserById",18);
            // 提交事务
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }

5.4,更新:

try {
            // 创建数据库会话实例sqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 添加用户信息
            User user = new User();
            user.setId(16);
            user.setUsername("张小明");
            user.setAddress("河南郑州");
            user.setSex("1");
            user.setPrice(1999.9f);
            sqlSession.update("test.updateUser", user);
            // 提交事务
            sqlSession.commit();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }

总结:增删改查,映射文件的标签是不同的,方法也是不同的。

补充:占位符用到一个ognl(对象图导航语言)的技术。

#{}和${}
#{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型,pojo、hashmap。
如果接收简单类型,#{}中可以写成value或其它名称。
#{}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。

${}表示一个拼接符号,会引用sql注入,所以不建议使用${}。
${}接收输入参数,类型可以是简单类型,pojo、hashmap。
如果接收简单类型,${}中只能写成value。
${}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。

 

 

posted @ 2018-05-02 18:32  假程序猿  阅读(475)  评论(1)    收藏  举报