MyBatis01

========

第一天:

  1. MyBatis的介绍

  2. MyBtais访问数据库的核心构成

  3. 搭建Mybatis开发环境

  4. MyBatis入门程序

    a. 增删改查的实现(重点)

    b. MyBatis与Hibernate的对比

    c. 插入的子查询

  5. Dao的开发方法

    a. 原始dao的开发方法

    b. 动态代理方式(重点)

  6. MyBatis核心配置文件说明

第二天:

  1. 高级的参数映射和返回值映射(重点)

    a. 高级参数映射

    b. 高级返回值映射

  2. 动态sql(重点)

  3. 关联查询结果(重点)

    a. 一对一关联结果

    b. 一对多关联结果

  4. Mybatis整合spring

  5. 逆向工程

MyBatis介绍

MyBatis前世今生

MyBatis前世今生

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software
foundation 迁移到了google
code,并且改名为MyBatis。2013年11月迁移到Github。

●【GitHub】

GitHub就是一个互联网上的超大SVN库,里面维护着许多开源项目的代码。任何人都可以把自己好多项目代码放上去共享,同时接受其他人的共同开发。

学习MyBatis的目的

1. 简化JDBC编程

MyBatis和Hibernate都是优秀的持久层框架,发明它们的目的之一就是简化jdbc编程。这些框架封装了JDBC操作的细节过程,大大简化了开发,让开发人员更好的集中精力实现业务逻辑。

2. 易于SQL优化

Hibernate完全的O/R映射使得我们没法根据实际业务优化SQL语句,所以无法满足特殊的业务需求和高性能业务,MyBatis正好弥补了这个空白。

确切点说MyBatis最重要的就是写好SQL,包括写好SQL语句和写好SQL映射

·SQL语句就是标准的SQL语句(可以在当前选用的数据库产品下,根据需求使用该产品下的SQL函数

·SQL映射包括:参数映射和返回值映射(返回值只针对查询,增删改是没有返回值的

●【参数映射】(也叫做【输入映射】)

MyBatis将java对象传入给SQL语句参数的过程。

●【返回值映射】(也叫做【输出映射】)

MyBatis将SQL查询的结果集处理成一个java对象并返回给java程序的过程。

●【java对象】

如果传递单个参数,变量可以是简单类型:int、long、float、String、Integer、Long、Boolean、Float等。

如果传递多个参数,必须使用实体类封装,封装后再传给sql。有一个名词叫pojo(Plain
Ordinary Java Object),里面有许多属性以及它们的getter/setter方法。

实体类==Java bean==pojo,它们没有区别,就是换种叫法而已。

● SQL语句以及映射写在xml或注解中。

MyBatis访问数据库的核心构成

我们通过Hibernate与MyBatis访问数据库核心构成的对比来学习MyBatis如何访问数据库的。

[]{#_MON_1572932917 .anchor}

从这个结构图要明确知道MyBatis访问数据库的核心构成包括三个核心类和两种配置文件。

三个核心类:SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession;

两种配置文件:MyBatis核心配置文件(一个)、MyBatis映射文件(多个)。

了解了核心构成,下面主要学习的就是SQL的映射规范了,也就是在MyBatis中如何写SQL。只要按照规范写SQL,MyBatis就能自动的帮助我们完成具体的映射工作。

MyBatis开发环境搭建

这个开发环境仅仅是学习使用的,并不是真正项目中使用的开发环境。

第一步:创建数据库和表

  1. 创建数据库【mybatis】,编码utf-8

  2. 导入【资料\01.数据库\initialDB.sql】脚本。

第二步:创建工程

创建一个普通java工程。

因为这里我们只是用来学习MyBatis,所以不是实际项目中真正的开发环境,因此建立一个普通java工程就够用了。

等都后面SSM整合之后才是我们真正在实际项目中使用的开发环境。

第三步:导入jar包

这里我们用到【MyBatis的jar包】和【mysql的jar包】。

  1. 取得MyBatis的jar包

Mybatis的GitHub地址:
https://github.com/mybatis/mybatis-3/releases

{width=”4.916666666666667in”
height=”2.2459962817147856in”}

  1. 导入MyBatis的jar包

1)Mybatis的jar包分为:


核心jar包 {width=”1.2307688101487315in” height=”0.8615387139107612in”} 依赖jar包 {width=”1.4514588801399826in” height=”1.1602569991251093in”}


2)导入工程并关联到工程

{width=”2.765152012248469in”
height=”2.9914807524059492in”}
{width=”1.7913385826771653in”
height=”3.043307086614173in”}

3. mysql的jar包也要导入工程并关联,方法同上。

{width=”2.5404133858267715in”
height=”0.30357174103237095in”}

第四步:框架的配置文件

核心配置文件

创建Source Folder【config】,它作为全部关于环境的配置文件的存放目录。

{width=”2.4107141294838144in”
height=”0.8557119422572178in”}

在config下创建MyBatisConfig.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>

<!– 数据库环境的配置(临时配置) –>

<!–

environments:表示多个数据库环境的配置标签

default:当前默认使用的数据库环境

–>

<environments default=“dev”>

<!– 开发数据库环境的配置 –>

<!–

environment:表示一个数据库环境配置的标签

id:表示唯一标识一个数据库环境

–>

<environment id=“dev”>

<!– 事务管理的配置 –>

<!–

transactionManager:表示事务管理的配置标签

type:表示事务管理的类型,由于MyBatis是对JDBC的封装,所以通常使用JDBC的事务

–>

<transactionManager type=“JDBC”/>

<!– 数据源配置:driver, url, username, password –>

<!–

dataSource:表示数据源的配置标签

type:表示数据源的类型,标识是否支持数据库连接池

POOLED:表示支持数据库连接池的数据源

UNPOOLED:表示不支持数据库连接池的数据源

–>

<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=“123”/>

</dataSource>

</environment>

</environments>

</configuration>


这个文件就是MyBatis的核心配置文件,里面主要配置连接数据库的数据源、事务管理以及MyBatis的映射文件有哪些。

这里大概知道里面配置的是数据库信息就可以了,因为到实际项目中框架整合后就不用这么配置了,所以它的实用价值不大,这里只是临时这么用一下。

SQL映射文件

根据业务需求,映射文件可以是多个,里面写具体的业务SQL和SQL映射(参数映射和返回值映射)。

  1. 创建SQL映射文件(因为SQL映射文件是关于业务的,所以不要放到config里面)

{width=”2.2395209973753283in”
height=”1.2575754593175854in”}

把下面的内容拷贝到这个配置文件中:注意第一行,别重复了。


<?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=“user”>

<!– SQL映射 –>

</mapper>


  1. 配置映射文件

在MyBatis核心配置文件中配置映射文件,目的是为了让MyBatis知道有这个映射文件。


<!DOCTYPE configuration

PUBLIC “-//mybatis.org//DTD Config 3.0//EN”

http://mybatis.org/dtd/mybatis-3-config.dtd“>

<configuration>

。。。。。。

<!– 配置映射文件 –>

<mappers>

<!– 通过映射文件在编译后类目录下的相对路径加载映射文件

resource:用来指定映射文件的相对路径

–>

<mapper resource=“cn/itcast/mapper/UserMapper.xml” />

</mappers>

</configuration>


第六步:测试开发环境

  1. 利用junit进行测试,eclipse本身包含junit的jar直接导入即可:过去无可挽回,未来可以改变

{width=”2.6287882764654418in”
height=”1.954737532808399in”}
{width=”2.025698818897638in” height=”2.0in”}
{width=”2.060605861767279in”
height=”2.012584208223972in”}

  1. 手动编写客户端测试程序

创建Source Folder【test】用于存放测试程序的,并创建一个普通的class:


package mybatis;

import java.io.InputStream;

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;

public class MyTest {

@Test

public void envTest() throws Exception {

SqlSession sqlSession = null;

try {

// 1. 读取配置文件(MyBatis有专门读取配置文件的工具类Resources)

InputStream inputStream = Resources.getResourceAsStream(“MyBatisCofig.xml”);

// 2. 根据主配置文件创建会话工厂

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 3. 根据会话工厂创建会话对象

// 业务层通过SqlSession对象来访问数据库进行CRUD操作,每个执行方法中会话对象要私有

sqlSession = sqlSessionFactory.openSession();

System.out.println(sqlSession);

} catch(Exception e) {

e.printStackTrace();

throw e;

} finally {

// 关闭会话

sqlSession.close();

}

}

}


注意:上面的核心类不是真实项目开发中需要写的,都是临时的写法。这里由于只使用了MyBatis框架,所以只能临时手动的加载核心配置文件、手动创建会话工厂以及会话对象,到真实项目中这些都不用写。这里只是做测试用的代码。大家只要知道三个核心类是什么就可以。

  1. 能够打印出SqlSession对象的信息说明客户端与数据库连接的会话已经建立起来了。

{width=”5.430555555555555in”
height=”0.25332020997375326in”}

第七步:给MyBatis加日志输出

把【资料\04.参考案例\config\log4j.properties】拷贝到工程的config目录下。

小结

这一节主要是操作,因此记忆的东西很少,主要是课后多加练习开发环境的搭建。

搭建开发环境的目录步骤可以看目录。

MyBatis入门程序

MyBatis入门程序开发分为两步:

1. 在SQL映射文件中写SQL以及SQL映射,包括参数映射和返回值映射。

2. 在客户端用SqlSession调用SQL执行。

查——根据id查用户(重点)

  1. 【UserMapper.xml】映射文件中增加查询SQL的映射配置:

<说明>(需要掌握)


项目 解释


<select> 表示是查询SQL

id 在相同映射文件中SQL的唯一标识(名称不允许包含点【.】

parameterType 传入参数的类型(当没有参数时可以省略)

resultType SQL返回给java的结果类型

#{userId} #{xxx}叫做占位符。Xxx是参数的变量名。

                   占位符作用:根据占位符中的变量名接收参数,并根据参数类型判断是否需要加单引号。

                   如果只写\#{}会报错。有了名称就不用考虑参数赋值的先后顺序了,比jdbc编程要方便很多。

<SQL示例>

注意:要严格按照MyBatis的要求和映射规范去写xml,否则MyBatis就无法解析了。


<?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=“user”>

<!– SQL –>

<!– 根据id查询用户信息 –>

<select id=“findUserById” parameterType=“int” resultType=“cn.itcast.pojo.User”>

SELECT

userId, name, mobile, sex, age, address

FROM user

WHERE

userId = #{userId}

</select>

</mapper>


<SQL映射规范>

·参数映射规范(一)

传单个参数时,parameterType=“简单类型”,占位符中的变量可以任意名称,但不能没有。

·返回值映射规范(一)

返回单条记录时,resultType=“pojo类型”,结果集的列名必须等于pojo的属性名。

(注意单条记录中的多个值不能分散的返回,MyBatis不支持)

  1. 【MyTest.java】中增加一个测试方法:

<说明>(需要掌握)

项目 解释


selectOne 查询单个记录(单值或单条都使用它)
第一个参数 namespace属性的值 + . + SQL id属性的值(namespace确定映射文件,id确定SQL)
第二个参数 传给SQL的参数,类型 = parameterType指定的类型(当没有参数时可省略)
返回值 SQL查询的结果,类型 = resultType指定的类型

<代码示例>


// 测试根据id查询用户信息

@Test

public void test1() throws Exception {

// 读取配置文件

InputStream inputStream = Resources.getResourceAsStream(“MyBatisConfig.xml”);

// 根据配置文件创建会话工厂

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 根据会话工厂创建会话对象

SqlSession sqlSession = sqlSessionFactory.openSession();

// 根据id查询用户信息

User userInfo = sqlSession.selectOne(“user.findUserById”, 1001);

System.out.println(userInfo);

sqlSession.close();

}


查——根据名称查用户(重点)

因为用户名是一个不确定的查询条件,因此多半在SQL采用模糊查询的方式进行条件匹配。

用占位符接收参数映射

  1. 【UserMapper.xml】映射文件中增加查询SQL配置

<!– 根据用户名查询用户信息(方式一:用占位符接收参数映射) –>

<select id=“findUserByUserName” parameterType=“String” resultType=“cn.itcast.pojo.User”>

SELECT

userId, name,mobile,sex,age,address

FROM user

WHERE

name LIKE #{userName}

</select>


<SQL映射规范>

·返回值映射规范(二)

返回多条记录时,resultType=“pojo类型(集合泛型)”,结果集的列名必须等于pojo的属性名。

  1. 【MyTest.java】中增加一个测试方法:

<说明>(需要掌握)


项目 解释


selectList 查询多条记录(返回List<pojo>类型的java对象)

第一个参数 同上

第二个参数 同上

返回值 SQL查询的List集合结果,List集合的泛型 = resultType指定的类型

               MyBatis内部会通过返回值映射产生多个java对象,这些对象会放到一个List对象中,每个对象的类型就是resultType配置的泛型,最终将List对象返回给java程序。

<代码示例>


// 测试根据用户名查询用户信息(方式一)

@Test

public void test1() throws Exception {

// 读取配置文件

InputStream inputStream = Resources.getResourceAsStream(“MyBatisConfig.xml”);

// 根据配置文件创建会话工厂

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 根据会话工厂创建会话对象

SqlSession sqlSession = sqlSessionFactory.openSession();

//根据用户名查询用户信息

List<User> userList = sqlSession.selectList(“user.findUserByUserName”, “%王%”);

System.out.println(userList);

sqlSession.close();

}


※Java程序中的%能不能写到SQL里面呢?

用字符串拼接符接收参数映射

  1. 【UserMapper.xml】映射文件中增加查询SQL配置

<说明>


项目 解释


${value} ${xxx}叫做字符串拼接符。

              拼接符特征:把SQL语句和参数值当成字符串进行***字符串的原样拼接***,所谓原样就是不做任何jdbc类型转换,原来什么样就拼成什么样。所以SQL配置中必须人为加单引号才行。

<SQL示例>


<!– 根据用户名查询用户信息(方式二:用拼接符接收参数) –>

<select id=“findUserByUserName2” parameterType=“String” resultType=“cn.itcast.pojo.User”>

SELECT

userId, name,mobile,sex,age,address

FROM user

WHERE

name LIKE ‘%${value}%’

</select>


<SQL映射规范>

·参数映射规范(二)

传单个参数时,parameterType=“简单类型”,拼接符中的变量名必须是value,但不能没有。

为什么是value这个固定的名字呢?

处理拼接符的java类是TextSqlNode.java,在其中处理参数的时候有这样的逻辑:

{width=”5.513888888888889in”
height=”2.4413681102362204in”}

从逻辑判断可以看出,如果参数是null或者是Java简单类型,直接用固定值value进行映射,其他类型在同OGNL表达式的名称进行映射,这就是为什么必须用value的原因了。

  1. 【MyTest.java】中增加一个测试方法:

// 测试根据用户名查询用户信息(方式二)

@Test

public void test1() throws Exception {

// 读取配置文件

InputStream inputStream = Resources.getResourceAsStream(“MyBatisConfig.xml”);

// 根据配置文件创建会话工厂

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 根据会话工厂创建会话对象

SqlSession sqlSession = sqlSessionFactory.openSession();

//根据用户名查询用户信息

List<User> userList = sqlSession.selectList(“user.findUserByUserName2”, “王”);

System.out.println(userList);

sqlSession.close();

}


占位符与拼接符区别

  1. 类型处理:

占位符#{}传递参数时会做参数类型处理,

拼接符${}传递参数时不会做类型处理只进行字符串原样拼接

  1. 安全性:

${}的原样拼接导致它存在安全漏洞,容易产生SQL注入风险

SELECT u.userId, u.name, u.age, u.address FROM user u WHERE u.name
LIKE ” or 1=1 OR 1=” sql注入

#{}的类型处理会对参数中存在的SQL敏感字符先转义然后再映射给SQL,这就不会影响原先的SQL,因此可以有效防止SQL注入。

  1. 工作中的应用:

由于拼接符${}存在安全隐患,因此在实际项目尽量使用占位符#{}

附一:SQL注入的一个示例(了解) {#附一sql注入的一个示例了解 .ListParagraph}


1 映射文件中的配置


<!– 用拼接符接收参数 –> <!– 用占位符接收参数 –>

<select id=“selectUserByUserName3” parameterType=“String” resultType=“cn.itcast.pojo.User”> <select id=“selectUserByUserName4” parameterType=“String” resultType=“cn.itcast.pojo.User”>

SELECT SELECT

u.userId, u.userId,

u.name, u.name,

u.age, u.age,

u.address u.address

FROM user u FROM user u

WHERE u.name LIKE ‘${value}’ WHERE u.name LIKE #{name}

</select> </select>

2 传递参数是一致的,左边拼接符最外面的单引号已经在映射文件中写上了;右边占位符按照预想由于传入的是String字符串类型的参数,所以会做类型处理自动的在参数外面加上一对单引号。但事情会想我们想象的那样进行吗?

List<User> userInfoList = sqlSession.selectList(“user. selectUserByUserName3“, “’ OR 1=1 OR 1=’”); List<User> userInfoList = sqlSession.selectList(“user. selectUserByUserName4“, “’ OR 1=1 OR 1=’”);

3 结果发现左边确实发生了sql注入,右边没有发生:

DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@462d5aee] DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@58c1670b]

DEBUG [main] - ==> Preparing: SELECT u.userId, u.name, u.age, u.address FROM user u WHERE u.name LIKE ” OR 1=1 OR 1=” DEBUG [main] - ==> Preparing: SELECT u.userId, u.name, u.age, u.address FROM user u WHERE u.name LIKE ?

DEBUG [main] - ==> Parameters: DEBUG [main] - ==> Parameters: ’ OR 1=1 OR 1=’(String)

DEBUG [main] - <== Total: 14 DEBUG [main] - <== Total: 0

[[1001, 王小一, null, 56, null, 南京], [1002, 王小二, null, 48, null, 青岛], [1003, 王小三, null, 32, null, 大连], [1004, 张三, null, 23, null, 广州], [1005, 王小五, null, 34, null, 重庆], [1006, 王小六, null, 31, null, 石家庄], [1007, 迎春, null, 28, null, 苏州], [1008, 张三, null, 23, null, 广州], [1009, 迎秋, null, 20, null, 长沙], [1010, 迎冬, null, 18, null, 佛山], [1011, 张三, null, 30, null, 广州], [1013, 张三, null, 30, null, 广州], [1014, 张三, null, 30, null, 广州], [1015, 张三, null, 30, null, 广州]] []

DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@462d5aee] DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@58c1670b]

DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@462d5aee] DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@58c1670b]

DEBUG [main] - Returned connection 1177377518 to pool. DEBUG [main] - Returned connection 1489069835 to pool.

左边拼接是原样拼接因此出现了漏洞,形成的SQL相当于mysql的工具中左边的样子:

右边占位符由于做类型处理,首先后把校验传入的参数是否有敏感字符,这里单引号就是一个敏感字符,其次如果有敏感字符需要进行转义,上面的参数转义为:\’ OR 1=1 OR 1=\’,再次再把转义完的参数映射给SQL并在参数外面加一对单引号,转义后的参数就不会对原先的SQL产生影响,仅仅被当作普通参数值进行处理。形成的SQL相当于mysql的工具中右边的样子:

{width=”3.4924234470691164in” height=”1.4468613298337707in”} {width=”3.4772725284339456in” height=”1.3909087926509187in”}


附二:如何防止SQL注入(了解) {#附二如何防止sql注入了解 .ListParagraph}

1. 尽量使用占位符,如果是模糊查询可以在java程序中拼接%

2. 如果是MySQL数据库,映射文件中的SQL可以像下面这样写:


<!– 根据用户名查询用户信息 –>

<select id=“selectUserByName” parameterType=“String” resultType=“cn.itcast.pojo.User”>

SELECT

userId,name,mobile,sex,age,address

FROM

User

WHERE

name LIKE “%”#{value}”%”

</select>


这样SQL可以解释成两个%常量和传入的参数值拼接成一个整体,再判断是否需要加单引号,这也能防止SQL注入,但是这个只限于MySQL好用,如果是Oracle,是不允”%”这样的写法的。

3. 在前端自定义SQL敏感字符集,然后进行提交前的参数校验。

查——用户表记录数(重点)

  1. 【UserMapper.xml】映射文件中增加查询SQL配置

<说明>

项目 解释


<select> 同上
id 同上
parameterType 没有参数时可以省略
resultType SQL返回给程序的java结果的类型

<SQL示例>


<!– 取得用户表的记录数 –>

<select id=“countUserRecord” resultType=“int”>

SELECT COUNT(userId) FROM user

</select>


注意:不要使用count(*),因为count(*)效率低,可以count(1)或count(字段名)都可以。

<SQL映射规范>

·返回值映射规范(三)

返回单值时,resultType=“简单类型”,值直接返回给java程序。

  1. 【MyTest.java】中增加一个测试方法:

// selectOne也可以返回单值结果

int count = ss.selectOne(“user.countUserRecord”);

System.out.println(count);


增——插入用户信息(重点)

  1. 【UserMapper.xml】映射文件中增加插入SQL配置

<说明>

项目 解释


<insert> 表示插入SQL的标签。
id 同查询
parameterType 同查询
resultType 插入SQL没有返回值,所以没有这个属性

<SQL示例>


<!– 插入用户信息 –>

<insert id=“addUserInfo” parameterType=“cn.itcast.pojo.User”>

INSERT INTO USER

(name,mobile,sex,age,address)

VALUES

(#{name},#{mobile},#{sex},#{age},#{address})

</insert>


<SQL映射规范>

·参数映射规范(三)

传多个参数时,parameterType=“pojo类型”,占位符或拼接符的变量名必须等于参数对象的属性名。

  1. 【MyTest.java】中增加一个测试方法:

<说明>

项目 解释


insert 插入处理
第一个参数 同查询
第二个参数 同查询
返回值 SQL没有返回值,但是方法本身有一个int的返回值,表示插入的记录条数。

<代码示例>


// 测试插入一条用户信息

@Test

public void test1() throws Exception {

SqlSession sqlSession = null;

try {

// 根据会话工厂创建会话对象

sqlSession = sqlSessionFactory.openSession();

User user = new User();

user.setAge(18);

user.setAddress(“北京”);

user.setMobile(“13500099000”);

user.setName(“张三”);

user.setSex(“男”);

// 插入用户信息

int count = sqlSession.insert(“user.addUserInfo”, user);

System.out.println(“count=” + count);

sqlSession.commit();

} catch(Exception ex) {

ex.printStackTrace();

sqlSession.rollback();

throw ex;

} finally {

sqlSession.close();

}

}


改——根据id更新用户(重点)

  1. 【UserMapper.xml】映射文件中增加插入SQL配置

<说明>

项目 解释


<update> 用于更新SQL的标签。
id 同查询
parameterType 同查询
resultType 更新SQL没有返回值,所以没有这个属性

<SQL示例>


<!– 根据id修改用户信息 –>

<update id=“updateUserById” parameterType=“cn.itcast.pojo.User”>

UPDATE user

SET

name = #{name},

mobile = #{mobile},

sex = #{sex},

age = #{age},

address = #{address}

WHERE

userId = #{userId}

</update>


<SQL映射规范>—同插入的规范。

  1. 【MyTest.java】中增加一个测试方法:

<说明>

项目 解释


update 更新处理
第一个参数 同查询
第二个参数 同查询
返回值 SQL没有返回值,但是方法本身有一个int的返回值,表示更新的记录条数。

<代码示例>


// 测试根据id修改用户信息

@Test

public void test1() throws Exception {

SqlSession sqlSession = null;

try {

// 根据会话工厂创建会话对象

sqlSession = sqlSessionFactory.openSession();

User user = new User();

user.setAddress(“天津”);

user.setAge(28);

user.setMobile(“13600099000”);

user.setName(“李四”);

user.setSex(“女”);

user.setUserId(1011);

// 更新用户信息

int count = sqlSession.update(“user.updateUserById”, user);

System.out.println(“count=” + count);

sqlSession.commit();

} catch(Exception ex) {

ex.printStackTrace();

sqlSession.rollback();

throw ex;

} finally {

sqlSession.close();

}

}


删——根据id删除用户(重点)

  1. 【UserMapper.xml】映射文件中增加插入SQL配置

<说明>

项目 解释


<delete> 用于删除SQL的标签。
id 同查询
parameterType 同查询
resultType 删除SQL没有返回值,所以没有这个属性

<SQL示例>


<!– 根据id删除用户信息 –>

<delete id=“deleteUserById” parameterType=“int”>

DELETE FROM user WHERE userId = #{id}

</delete>


<SQL映射规范>—同查询的规范。

  1. 【MyTest.java】中增加一个测试方法:

<说明>

项目 解释


delete 删除处理
第一个参数 同查询
第二个参数 同查询
返回值 SQL没有返回值,但是方法本身有一个int的返回值,表示删除的记录条数。

<代码示例>


// 测试根据id删除用户信息

@Test

public void test1() throws Exception {

SqlSession sqlSession = null;

try {

// 根据会话工厂创建会话对象

sqlSession = sqlSessionFactory.openSession();

// 根据id删除用户信息

int count = sqlSession.delete(“user.deleteUserById”, 1011);

System.out.println(“count=” + count);

sqlSession.commit();

} catch(Exception ex) {

ex.printStackTrace();

sqlSession.rollback();

throw ex;

} finally {

sqlSession.close();

}

}


增删改查小结(重点)

本小结下的内容都是重点需要重点记忆。

<SQL映射规范>

·参数映射规范

传单个参数时,parameterType=“简单类型”,占位符的变量名可以任意,拼接符的变量名必须是value,但不能没有。

传多个参数时,parameterType=“pojo类型”,占位符或拼接符的变量名必须等于参数对象的属性名。

·返回值映射规范

返回单值时,resultType=“简单类型”,值直接返回给java程序。


返回单/多条记录时 单:resultType=“pojo类型” 结果集列名必须等于pojo的属性名

                  多:resultType=*"pojo类型(集合泛型)"*   

<增删改查对应的标签和java客户端调用的方法>


区分 标签 客户端调用方法


增 <insert> insert(namespace名+.+sql id, sql的参数变量)

删 <delete> delete(参数同上)

改 <update> update(参数同上)

查 <select> 单值或单条selectOne(参数同上)

                          多条selectList(参数同上)

MyBatis与Hibernate对比总结

[]{#_MON_1563479254 .anchor}

插入标签中的子查询(了解)

插入的子查询主要是用来取得插入数据主键的。

功能一: 取得插入数据的自增主键

selectKey +
LAST_INSERT_ID(),可以解决如何查询自增型主键的数据表中刚插入记录的主键的问题。先插入后取得,取得后可以和其他表做外键关联的操作。

  1. 【UserMapper.xml】映射文件中增加插入SQL配置

<说明>

项目 解释


<selectKey> 用于<insert>操作的子查询。
order 子查询相对于insert SQL的执行顺序(AFTER:在插入之后执行 BEFORE:在插入之前执行)
keyProperty 传入的java对象参数的某个属性名,用于将子查询结果赋值给参数对象的指定属性。这样在java程序中只要执行完insert()方法,就可以从参数对象中指定的属性上取得这个子查询的结果。
resultType 子查询的值按照什么类型返回结果
LAST_INSERT_ID() mysql的函数,可以返回最新插入记录的主键,要和insert语句配合使用,否则单独执行的值就是0

<SQL示例>


<!– 插入一条用户信息并返回新插入记录的主键 –>

<insert id=“addUserInfo” parameterType=“cn.itcast.pojo.User”>

<!– 插入操作中的子查询 –>

<selectKey order=“AFTER” keyProperty=“userId” resultType=“int”>

SELECT LAST_INSERT_ID()

</selectKey>

INSERT INTO USER

(name,mobile,sex,age,address)

VALUES

(#{name},#{mobile},#{sex},#{age},#{address})

</insert>


  1. 【MyTest.java】中增加一个测试方法:

// 测试插入一条用户信息

@Test

public void test1() throws Exception {

SqlSession sqlSession = null;

try {

// 根据会话工厂创建会话对象

sqlSession = sqlSessionFactory.openSession();

User user = new User();

user.setAge(18);

user.setAddress(“北京”);

user.setMobile(“13500099000”);

user.setName(“张三”);

user.setSex(“男”);

System.out.println(“user.userId=” + user.getUserId());

// 插入用户信息

int count = sqlSession.insert(“user.addUserInfo”, user);

System.out.println(“count=” + count);

System.out.println(“user.userId=” + user.getUserId());

sqlSession.commit();

} catch(Exception ex) {

ex.printStackTrace();

sqlSession.rollback();

throw ex;

} finally {

sqlSession.close();

}

}


功能二: 使用UUID实现主键

selectKey +
UUID(),可以解决非自增型主键的数据表中在插入数据前先创建主键的问题。

<说明>

项目 解释


<selectKey> 同上
order 同上(这里指定为BEFORE)
keyProperty 同上
resultType 同上
UUID() mysql的函数,可以返回随机的UUID,可以作为主键用。

映射文件:

{width=”1.5897440944881889in”
height=”1.3600120297462817in”}


<!– 取得插入数据的主键后插入数据 –>

<insert id=“insertOrderData” parameterType=“cn.itcast.pojo.Order”>

<selectKey order=“BEFORE” keyProperty=“orderId” resultType=“String”>

SELECT UUID()

</selectKey>

INSERT INTO order1

(orderId, userId, orderStatus, goodsId, createDateTime)

VALUES

(#{orderId}, #{userId}, #{orderStatus}, #{goodsId}, now());

</insert>


客户端程序:


// 数据库操作…

// insert:表示插入SQL的方法

Order order = new Order();

order.setGoodsId(“123456789”);

order.setOrderStatus(“01”);

order.setUserId(1008);

System.out.println(order.getOrderId());

ss.insert(“order.insertOrderData”, order);

System.out.println(order.getOrderId());

ss.commit();


附一:JDBC的问题 {#附一jdbc的问题 .ListParagraph}

在java的世界里访问数据库最底层就是用JDBC,它是原生的数据库开发。JDBC对于单机版的软件或者一个小办公室的小系统都还是可以应付的,毕竟业务简单,数据量小,程序规模都不大。修改、编译、发布都很容易。但随着业务发展这样的IT技术不能满足要求了。逐渐的暴露出一些问题。


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”, “root”);

//定义sql语句 ?表示占位符

String sql = “select * from user where username = ? and age = ?”;

//获取预处理statement

preparedStatement = connection.prepareStatement(sql);

//设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值

preparedStatement.setString(1, “王五”);

preparedStatement.setInt(2, 18);

//向数据库发出sql执行查询,查询出结果集

resultSet = preparedStatement.executeQuery();

List<User> userList = new ArrayList<User>();

//遍历查询结果集

while(resultSet.next()){

User user = new User();

user.setUserId(resultSet.getInt(“id”));

user.setName(resultSet.getString(“username”));

userList.add(user);

}

} 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();

}

}

}

}


从上面的问题总结如下:

  1. 数据库连接的创建、释放频繁造成系统资源浪费,缺乏有效管理,非常影响系统性能。

如果使用数据库连接池可解决此问题。

  1. 程序中存在硬编码:(硬编码就是写死在程序中的固定值)

1) 数据库连接字符串:换数据库就要改代码,就要重新编译发布,维护压力增大。

2) SQL定义字符串:SQL修改的可能性较大,修改SQL就要重新编译发布,维护压力增大。

3) 传参数时的参数位置:参数必须按照先后顺序设置到对应的SQL条件上,十分不灵活。

4) 结果集中字段名字符串:字段名变化就要改代码,就要重新编译发布,维护压力增大。

附二:MyBatis对JDBC问题的解决 {#附二mybatis对jdbc问题的解决 .ListParagraph}

  1. 如何解决JDBC数据连接资源缺乏管理的问题?

解决:在MyBatis配置文件中配置了数据库连接池。

  1. 如何解决SQL的硬编码

解决:将Sql语句配置在SQL映射文件中与java代码分离。

  1. 如何解决SQL参数的顺序硬编码问题

解决:MyBatis的参数映射,可以帮我们把java对象自动的映射给SQL

  1. 如何解决结果集中字段名字符串的硬编码

解决:MyBatis的返回值映射,可以帮我们把结果集自动的映射给java对象。

DAO开发方法

传统DAO开发方式

传统的DAO开发方式就是编写DAO接口和DAO实现类来实现对数据库的访问。

编写SQL

从【UserMapper.xml】中挑选两个SQL


<!– 根据id查询用户信息 –>

<select id=“findUserById” parameterType=“int” resultType=“cn.itcast.pojo.User”>

SELECT

userId, name, mobile, sex, age, address

FROM user

WHERE

userId = #{userId}

</select>

<!– 根据用户名查询用户信息 –>

<select id=“findUserByUserName2” parameterType=“String” resultType=“cn.itcast.pojo.User”>

SELECT

userId, name,mobile,sex,age,address

FROM user

WHERE

name LIKE ‘%${value}%’

</select>


编写DAO接口

在【cn.itcast.dao】包下创建DAO接口【UserDao】


package cn.itcast.dao;

import java.util.List;

import cn.itcast.pojo.User;

public interface UserDao {

// 根据id查询用户信息

public User findUserById(Integer id) throws Exception;

// 根据姓名查询用户信息

public List<User> findUserByName(String name) throws Exception;

}


编写DAO接口实现类

在【cn.itcast.dao】包下创建接口【UserDao】的实现类【UserDaoImpl】


package cn.itcast.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import cn.itcast.pojo.User;

public class UserDaoImpl implements UserDao {

private SqlSessionFactory sqlSessionFactory;

public void setSqlSessionFactory(SqlSessionFactory sqlSf) {

this.sqlSessionFactory = sqlSf;

}

@Override

public User findUserById(Integer id) throws Exception {

SqlSession sqlSession = null;

try {

sqlSession = sqlSessionFactory.openSession();

// 根据id查询

User userInfo = sqlSession.selectOne(“user.findUserById”, id);

return userInfo;

} catch(Exception ex) {

ex.printStackTrace();

throw ex;

} finally {

sqlSession.close();

}

}

@Override

public List<User> findUserByName(String name) throws Exception {

SqlSession sqlSession = null;

try {

// 根据会话工厂创建会话对象

sqlSession = sqlSessionFactory.openSession();

// 根据用户名查询

List<User> userInfoList = sqlSession.selectList(“user.findUserByUserName2”, name);

return userInfoList;

} catch(Exception ex) {

ex.printStackTrace();

throw ex;

} finally {

sqlSession.close();

}

}

}


编写客户端测试程序

在【test】目录下创建【MyTest2.java】测试程序


package mybatis;

import java.io.InputStream;

import java.util.List;

import org.apache.ibatis.io.Resources;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import org.junit.Before;

import org.junit.Test;

import cn.itcast.dao.UserDaoImpl;

import cn.itcast.pojo.User;

/**

* DAO开发方式

*/

public class MyTest2 {

private SqlSessionFactory sqlSessionFactory;

private UserDaoImpl userDao;

// 测试初始化函数

@Before

public void init() throws Exception {

// 读取配置文件

InputStream inputStream = Resources.getResourceAsStream(“MyBatisConfig.xml”);

// 根据主配置文件创建会话工厂

sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

userDao = new UserDaoImpl();

userDao.setSqlSessionFactory(sqlSessionFactory);

}

// 测试根据id查询用户信息

@Test

public void test1() throws Exception {

User user = userDao.findUserById(1001);

System.out.println(user);

}

// 测试根据用户名查询用户信息

@Test

public void test2() throws Exception {

List<User> userList = userDao.findUserListByName(“迎”);

System.out.println(userList);

}

}


传统DAO开发方法的问题

正常的传统DAO接口的实现类中各个方法的逻辑基本相同,代码重复的部分较多。除非有特殊业务要求才会加入特殊的业务逻辑,否则实现类中的方法几乎一致。

MyBatis动态代理DAO开发方式(重点)

什么是MyBatis动态代理

MyBatis打破传统DAO的开发方式,不需要程序员再写DAO的实现类了,可以直接用DAO接口的对象调用数据库处理的方法,MyBatis在执行时由代理对象代替DAO接口的实现类执行数据库处理。

要使用MyBatis动态代理就必须遵守动态代理的开发规范,即四个相等。

MyBatis动态代理开发规范

(需要掌握)

接口 映射文件


完全限定名 = Namespace的值
方法名 = SQL的id的值
接口方法的参数类型 = parameterType的值
接口方法的返回值类型 = resultType的值

编写SQL映射文件并添加配置

在【cn.itcast.mapper】包下创建新的映射文件【UserMapper2.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:整个MyBatis管理的映射文件中必须唯一 –>

<mapper namespace=“cn.itcast.dao.UserDao2”>

<!– SQL –>

<!– 根据id查询用户信息 –>

<select id=“findUserById” parameterType=“int” resultType=“cn.itcast.pojo.User”>

SELECT

userId, name, mobile, sex, age, address

FROM user

WHERE

userId = #{userId}

</select>

<!– 根据用户名查询用户信息 –>

<select id=“findUserByUserName” parameterType=“String” resultType=“cn.itcast.pojo.User”>

SELECT

userId, name,mobile,sex,age,address

FROM user

WHERE

name LIKE ‘%${value}%’

</select>

</mapper>


编写DAO接口

在【cn.itcast.dao】包下创建DAO接口【UserDao2】


package cn.itcast.dao;

import java.util.List;

import cn.itcast.pojo.User;

public interface UserDao2 {

// 根据id查询用户信息

public User findUserById(Integer id) throws Exception;

// 根据姓名查询用户信息

public List<User> findUserByUserName(String name) throws Exception;

}


编写客户端测试程序

在【MyTest2.java】测试程序中增加两个测试方法

<说明>

项目 解释


getMapper 生成接口的代理对象。
参数 接口的类型描述。(Xxxx.class
返回值 接口类型的代理对象。

<示例代码>


// 测试根据id查询用户信息(动态代理DAO开发方式)

@Test

public void test3() throws Exception {

SqlSession sqlSession = sqlSessionFactory.openSession();

// 用getMapper取得自动生成的DAO接口的实现类

UserDao2 userDao = sqlSession.getMapper(UserDao2.class);

User userInfo = userDao.findUserById(1001);

System.out.println(userInfo);

sqlSession.close();

}

// 测试根据id查询用户信息(动态代理DAO开发方式)

@Test

public void test4() throws Exception {

SqlSession sqlSession = sqlSessionFactory.openSession();

UserDao2 userDao = sqlSession.getMapper(UserDao2.class);

List<User> userList = userDao.findUserByUserName(“迎”);

System.out.println(userList);

sqlSession.close();

}


动态代理DAO开发方式的好处

Mybatis动态代理的好处就是简化开发,减少bug出现,让我们能集中精力写SQL。

传统DAO开发方式与MyBatis动态代理的区别

传统方式需要编写DAO接口的实现类,并通过实例化实现类的对象来访问数据库。

动态代理不需编写DAO接口的实现类,并通过接口类的代理对象来访问数据库。代理对象由MyBatis自动生成。

小结

本章的重点是MyBatis动态代理DAO开发方式,要掌握如何开发动态代理DAO,牢记动态代理四个开发规范。这也是今天课程中第二个重点。

MyBatis核心配置文件

核心配置文件配置项结构

官方说明Url:http://www.mybatis.org/mybatis-3/zh/configuration.html

{width=”3.926324365704287in”
height=”3.273584864391951in”}

·注意:配置项必须按照上面的顺序配置

红框中的为常用配置项,需要知道,其他配置项不常用,今后工作中用到时再查资料了解。

properties(属性)配置项

properties文件的好处

  1. 容易维护:相比于xml,properties文件更易于修改,降低修改时出错的几率。在实际项目中经常把properties属性文件与xml配置文件结合使用,把真正的值都放在properties属性文件中,在xml中使用的时候直接引过来就可以使用了,非常方便。

  2. 一处修改多处生效

properties标签配置


<!– 属性文件的配置 –>

<!–

properties:表示属性文件配置的标签

resource:表示类的相对路径下的java属性文件

url:表示文件的绝对路径

–>

<properties resource=“jdbc.properties” />


对应数据源配置的修改:


<!– 数据库环境的配置 –>

<environments default=“dev”>

<!– 开发数据库环境的配置 –>

<environment id=“dev”>

<!– 事务管理的配置 –>

<transactionManager type=“JDBC”/>

<!– 数据源配置:driver, url, username, password –>

<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>


properties属性文件中key的命名方式:

文件名 + . +
具体的key名,这样做的好处是不容易造成两个properties文件中出现同名的key。key一旦同名,这两个properties文件又都被同时引入到一个xml中就会出错。

typeAliases(类型别名)配置项

MyBatis内建的java类型的别名

{width=”7.268055555555556in”
height=”6.618055555555555in”}

注意:

红色框中的才是java基本类型以及对应的别名,从中可以看出java基本类型的别名都是以下划线开头的。

蓝色框中的是java基本类型的包装类以及对应的别名,这也是我们前面一直在使用的。

没有画框的都是正常使用的别名。

为什么在MyBatis内建议使用java基本类型的包装类型来传递参数?

MyBatis在处理参数时,如果传递的是java基本类型:int、long、char、bool等,MyBatis都会将基本类型包装成:Integer、Long、String、Boolean等,然后再进行传递,这样做的好处是避免java基本类型的默认值问题。Java基本类型参数在没有实际赋值时都会有默认值,如果你不主动给参数变量赋值就直接传给SQL,就会把参数变量的默认值传给SQL语句,这样就可能造成破坏业务数据的风险,因此在MyBatis内部这样的参数都会自动的被包装成对象进行传值。对象的好处是一旦没有传值,由于是对象,它的默认值就是null,给SQL传递null时一般都不会执行成功。

因此, 为了数据安全尽量使用蓝框的包装类型来传值赋值.

别名都是大小写不敏感的

int和INT、iNt、Int等都是一样的,其它的也一样不区分大小写

别名的目的

简化映射文件的配置,缩短配置项的长度

POJO类型的自定义别名配置格式

MyBatisConfig.xml中(注意主配置文件中的项目是有顺序的)


<!– 自定义别名 –>

<typeAliases>

<typeAlias type=“cn.itcast.pojo.User” alias=“User”/>

</typeAliases>


映射文件:


<!– 根据id查询用户信息 –>

<select id=“findUserById” parameterType=“int” resultType=“User”>

select

userId, name,mobile,sex,age,address

FROM user

WHERE

userId = #{userId}

</select>


结论

对于java本身拥有的类型可以使用别名,而自定义类型不要使用别名。

推荐使用java包装类型或它们的别名(篮框中的),而不要使用java基本类型和它们的别名(红框中的)。

mappers(映射文件)配置项

映射文件配置形式一

直接指定映射文件在类根目录下的相对路径

<说明>

项目 解释


<mappers> 用于SQL映射文件的配置,下面可以配置多个映射文件的扫描
<mapper><mappers>的子标签,用于一个映射文件的配置
resource 映射文件相对于类根目录下的相对路径

配置:


<!– 配置映射文件 –>

<mappers>

<mapper resource=“cn/itcast/mapper/UserMapper.xml”/>

<mapper resource=“cn/itcast/mapper/UserMapper2.xml”/>

</mappers>


映射文件配置形式二

通过一个java接口的完全限定名加载映射文件。

重新创建一个包【cn.itcast.mapper2】,在其中创建两个映射文件和两个对应同名的DAO接口,然后进行试验。

<说明>

项目 解释


class 接口的完全限定名
要求 DAO接口文件与映射文件必须同目录同名,接口完全限定名与映射文件的namespace必须相等

配置:


<!– 配置映射文件 –>

<mappers>

<mapper class=“cn.itcast.mapper2.UserMapper” />

</mappers>


映射文件配置形式三

<说明>

项目 解释


<package> 是第二种形式的批量加载形式
name 指定扫描的包路径
要求 指定包下DAO接口文件与映射文件必须同名同目录,接口完全限定名与映射文件的namespace必须相等

配置:


<!– 配置映射文件 –>

<mappers>

<package name=“cn.itcast.mapper2”/>

</mappers>


三种形式的辨析

  1. 形式一:

比较死板,一旦路径写错了不容易发现。一次只能加载一个映射文件。

  1. 形式二:

比较灵活,能检验是否写错(按住ctrl键,鼠标指针放上去有连接说明写的没有错)。SM整合后可以放到spring容器中。

  1. 形式三:

灵活更实用,是形式二的批量方式,SM整合后可以放到spring容器中。

小结

本章内容作为了解,看到了要知道配置的都是什么东西,不需要死记硬背。能背着大概说出MyBatis核心配置文件经常配置什么内容即可,不用说的很细致。当然如果能细致说出更好。

猜你喜欢

转载自blog.csdn.net/qq_32332777/article/details/79017637