mybatis总概览(1)(概念,增删改查例子)
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>
总结:
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(); } } } }
上面折叠代码还包括了:模糊查询,单个结果集合和列表结果集。
有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>
添加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”
这种映射目的:不仅仅是为了得到查询结果,实际目的是数据库中生成自增主键,并且在代码对象中得到主键。
测试代码:

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”
插入主键总结:映射的关键标签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读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。

浙公网安备 33010602011771号