mybatis初识

MyBatis从入门到最后会讲解的内容:

基础知识(重点,内容量多)

对原生态jdbc程序(单独使用jdbc开发)问题总结
mybatis框架原理 (掌握)
mybatis入门程序
    用户的增、删、改、查
mybatis开发dao两种方法:
    原始dao开发方法(程序需要编写dao接口和dao实现类)(掌握)
    mybaits的mapper接口(相当于dao接口)代理开发方法(掌握)
mybatis配置文件SqlMapConfig.xml
mybatis核心:
    mybatis输入映射(掌握)
    mybatis输出映射(掌握)
mybatis的动态sql(掌握)

高级知识

订单商品数据模型分析
高级结果集映射(一对一、一对多、多对多)
mybatis延迟加载
mybatis查询缓存(一级缓存、二级缓存)
mybaits和spring进行整合(掌握)
mybatis逆向工程

创建MySQL数据

导入下边脚本

下载地址

sql_table.sql:记录表结构。
sql_data.sql:可以理解为记录测试数据。在实际企业开发中,相当于提供了一个初始化数据脚本。

下面来回顾一下JDBC里面的原始代码:

    @Test
    public void showUser(){

        //数据库连接
        Connection connection = null;
        //预编译的Statement,使用预编译的Statement提高数据库性能
        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();
                }
            }

        }

    }

JDBC编程步骤:

1、 加载数据库驱动
2、 创建并获取数据库链接
3、 创建jdbc statement对象
4、 设置sql语句
5、 设置sql语句中的参数(使用preparedStatement)
6、 通过statement执行sql并获取结果
7、 对sql执行结果进行解析处理
8、 释放资源(resultSet、preparedstatement、connection)

JDBC问题总结:

1.数据库连接,使用时创建,不适用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响 数据库性能。

设想:使用数据库连接池管理数据库连接。

2.将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利系统维护。

设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。

3.向PreparedStatement中设置参数,对占位符位置和设置参数值,硬编码在java代码中,不利于系统维护。

设想:将sql语句及占位符号和参数全部配置在xml中。

4.从resultSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。

设想:将查询的结果集,自动映射成java对象。

Mybatis框架

Mybatis是什么??

  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。
  • MyBatis 以前是在apache官网下载,之后被托管到googlecode下,再后来托管到GitHub下(https://github.com/mybatis/mybatis-3/releases)。
  • MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
  • Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
  • 其中比较重要的一点就是:1.MyBatis 让程序员主要精力放在sql上,通过MyBatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql) 满足需要的sql语句。2.MyBatis 可以将向 PreparedStatement 中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)

MyBatis框架思路:

这里写图片描述

现在我们有个需求:

本篇博客就将按照下面内容作为驱动案例讲解:

1.根据用户id(主键)查询用户信息
2.根据用户名称模糊查询用户信息
3.添加用户
4.删除 用户
5.更新用户

开发环境配置:

第一步:

导入jar包。

下载jar包

其中你会看到的目录结构:

这里写图片描述

在lib目录下的jar包:

这里写图片描述

把核心包和依赖包都导入。

还有别忘了导入MySQL驱动包。

下载地址

第二步:

  • log4j.properties

在classpath下创建log4j.properties,内容如下:

classpath就是src下边

# Global logging configuration
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

这里写图片描述

第三步:

配置SqlMapConfig.xml

在src下创建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和environment对应的属性default和id能够填入的值:
        work:工作模式
        development:开发模式
        注意:两个属性值不能够乱写
    -->
    <environments default="development">
        <!--
            这里可以配置多个环境:
                为什么可以配置多个环境?
                因为在大型项目的时候可能会出现跨数据库存值取值的情况。
                比如:我在北京数据库里面取出值,然后在上海数据库里面删除值。这有可能发生的。这种类型叫做JTA,即Java Transaction API。
        -->
        <environment id="development">
        <!-- 使用jdbc事务管理,事务控制由MyBatis-->
            <transactionManager type="JDBC" />
        <!-- 配置数据源,其中数据源的实现是使用数据库连接池提供,由MyBatis管理-->
            <dataSource type="POOLED">
            <!-- 
                数据源:
                    POOLED:表示使用连接池,自带的。
                    UNPOOLED:不使用连接池,每次使用连接后就断开。
                    MANAGED:使用容器管理。笔者也不知道这是什么。。
             -->
                <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="" />
            </dataSource>
        </environment>
    </environments>
</configuration>

第四步:

我们这里是查询User表内容,所以我们这里创建用户类:

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private Integer sex;
    private String address;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public Integer getSex() {
        return sex;
    }
    public void setSex(Integer sex) {
        this.sex = sex;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address="
                + address + "]";
    }
}

第五步:

编写用户类的映射文件。

映射文件命名:
User.xml(原始ibatis命名),mapper代理开发映射文件名称叫XXXMapper.xml,比如:UserMapper.xml、ItemsMapper.xml

我这里User类的映射文件名称为User.xml
在映射文件中配置sql语句。

<?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进行分类化管理,理解sql隔离 
注意:使用mapper代理方法开发,namespace有特殊重要的作用,后面会讲到
-->
<mapper namespace="test">
    <!-- 在 映射文件中配置很多sql语句 -->
    <!-- 需求:通过id查询用户表的记录 -->
    <!-- 通过 select执行数据库查询
    id:标识 映射文件中的 sql
    将sql语句封装到mappedStatement对象中,所以将id称为statement的id
    parameterType:指定输入 参数的类型,这里指定int型 
    #{}表示一个占位符号
    #{id}:其中的id表示接收输入 的参数,参数名称就是id,如果输入 参数是简单类型,#{}中的参数名可以任意,可以value或其它名称

    resultType:指定sql输出结果 的所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象。
     -->
    <select id="findUserByid" parameterType="int" resultType="cn.domarvel.entity.User">
        select * from user where id=#{id}
    </select>
</mapper>

第六步:

在MyBatis核心配置文件中加载映射文件

    <!-- 加载映射文件 -->
    <mappers>
        <mapper resource="cn/domarvel/entity/User.xml"></mapper>
    </mappers>

整体MyBatis的核心配置文件内容:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 和spring整合后 environments配置将废除-->
    <!-- 
        下面的两个标签environments和environment对应的属性default和id能够填入的值:
        work:工作模式
        development:开发模式
        注意:两个属性值不能够乱写
    -->
    <environments default="development">
        <environment id="development">
        <!-- 使用jdbc事务管理,事务控制由MyBatis-->
            <transactionManager type="JDBC" />
        <!-- 数据库连接池,由MyBatis管理-->
            <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="" />
            </dataSource>
        </environment>
    </environments>

    <!-- 加载映射文件 -->
    <mappers>
        <mapper resource="cn/domarvel/entity/User.xml"></mapper>
    </mappers>
</configuration>

第七步:

编写测试代码

    @Test
    public void findUserById() {
        SqlSession sqlSession=null;
        try {
            //以流的方式获取MyBatis的配置文件
            InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
            //创建会话工厂,传入MyBatis的配置文件信息
            SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
            //通过工厂得到SqlSession
            sqlSession=sqlSessionFactory.openSession();
            // 通过SqlSession操作数据库
            // 第一个参数:映射文件中statement的id,等于=namespace.statement的id
            // 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
            // sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
            // selectOne查询出一条记录
            User user=sqlSession.selectOne("test.findUserByid", 1);

            System.out.println(user);

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            // 释放资源
            sqlSession.close();
        }
    }

可以不通过命名空间.id加载sql语句

通过命名空间.id访问配置文件的sql语句不是绝对的,可以不通过命名空间.id访问,如果id是唯一的,那么命名空间可以省略。
例如:

    <select id="findUserByid" parameterType="int" resultType="cn.domarvel.entity.User">
        select * from user where id=#{id}
    </select>
//如果findUserByid是唯一的
//那么可以在调用的时候把命名空间省略掉
  • 1
  • 2
  • 3
  • 4
  • 5

这是笔者当前的工作目录:

这里写图片描述
 

根据用户名称模糊查询用户信息:

在User.xml中进行配置以下内容:

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

测试代码:

    @Test
    public void findUserByLikeName(){
        SqlSession sqlSession=null;
        try {
            //以流的方式获取MyBatis的配置文件
            InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
            //创建会话工厂,传入MyBatis的配置文件信息
            SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
            //通过工厂得到SqlSession
            sqlSession=sqlSessionFactory.openSession();
            // 通过SqlSession操作数据库
            // 第一个参数:映射文件中statement的id,等于=namespace.statement的id
            // 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
            // sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
            // selectOne查询出一条记录
            List<User> list =sqlSession.selectList("test.findUserByLikeName", "小明");

            for(User i:list){
                System.out.println(i);
            }
            // 释放资源
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

这个是当前测试代码,MyBatis发出的sql底层代码。很明显通过${value} 这个关键字符串起的作用是把字符串和字符串拼接起来,并且没有用到占位符。所以很容易引起sql注入攻击。所以不建议使用${}

DEBUG [main] - ==>  Preparing: select * from user where username like '%小明%' 
  •  

下面来讲个区别(重点):

需求:我们想要在数据表里面找到“陈小明”这个User并且输出。

有两种实现:

    @Override
    public User findUserByNameMap(String name) throws Exception {
        SqlSession sqlSession=sqlSessionFactory.openSession();
        Map<String, User> resultsMap=sqlSession.selectMap("test.findUsers", "username");//第一个是声明操作id,第二个是指定表中的哪儿一个列名为键。
        //查询结果为map,指定列名为键,查询结果为值的map
        return resultsMap.get(name);
    }

    @Override
    public User findUserByNameList(String name) throws Exception {
        SqlSession sqlSession=sqlSessionFactory.openSession();
        List<User> users=sqlSession.selectList("test.findUsers");
        for(User i:users){
            if(i.getUsername().equals(name)){
                return i;
            }
        }
        return null;
    }

测试代码:

    @Test
    public void findUserByNameMap() throws Exception{
        System.out.println(userDao.findUserByNameMap("陈小明"));
    }
    @Test
    public void findUserByNameList() throws Exception{
        System.out.println(userDao.findUserByNameList("陈小明"));
    }

看了上面的代码实现,我就想问,哪儿一个效率比较高??

其实我一直都没有怎么注意这些,但是我被问起来的时候,就发现了它的重要性。

很明显,根据Map查询的时候效率高!!因为我是直接根据Map索引取值的。而List是还要遍历判断是否为需要的值,这是很浪费时间的。

Map的数据结构是散列表,也就是Hash表。

给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

而Hash表一般就是下面这个结构,数组+链表。其中数组会自动扩容。

这里写图片描述

添加用户:

编写User.xml用户映射文件:

    <!-- 添加用户 
    pojo是自定义类型的意思。
    parameterType:指定输入 参数类型是pojo(包括 用户信息)
    #{}中指定pojo的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值
    -->
    <insert id="insertSingleUser" parameterType="cn.domarvel.entity.User">
        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>

测试代码:

    @Test
    public void insertIntoUser(){
        SqlSession sqlSession=null;
        try {
            //以流的方式获取MyBatis的配置文件
            InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
            //创建会话工厂,传入MyBatis的配置文件信息
            SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
            //通过工厂得到SqlSession
            sqlSession=sqlSessionFactory.openSession();
            // 通过SqlSession操作数据库
            // 第一个参数:映射文件中statement的id,等于=namespace.statement的id
            // 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
            // sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
            // selectOne查询出一条记录
            sqlSession.insert("test.insertSingleUser", new User("FireLang", new Date(), 1, "LangIsland"));

            //查询值的时候事务将会自动提交。添加值的时候事务是不会自动提交的,如果我们要提交事务需要自己手动提交。不提交事务,数据插入将不会成功。
            sqlSession.commit();

            // 释放资源
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

DEBUG信息:

DEBUG [main] - ==>  Preparing: insert into user(username,birthday,sex,address) values(?,?,?,?) 
DEBUG [main] - ==> Parameters: FireLang(String), 2017-04-04 15:20:10.507(Timestamp), 1(Integer), LangIsland(String)

自增主键返回:

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

是insert之后调用此函数。

我们要实现把自增主键查询后放入到User对象里面。

User.xml配置文件配置:

    <!-- 添加用户 
    pojo是自定义类型的意思。
    parameterType:指定输入 参数类型是pojo(包括 用户信息)
    #{}中指定pojo的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值
    -->
    <insert id="insertSingleUser" parameterType="cn.domarvel.entity.User">
        <!-- 
        将插入数据的主键返回,返回到user对象中

        SELECT LAST_INSERT_ID():得到刚insert进去记录的主键值,只适用与自增主键

        keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
        order:SELECT LAST_INSERT_ID()执行顺序,相对于insert语句来说它的执行顺序
        resultType:指定SELECT LAST_INSERT_ID()的结果类型
         -->
        <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>

测试代码:

    @Test
    public void insertIntoUser(){
        SqlSession sqlSession=null;
        try {
            //以流的方式获取MyBatis的配置文件
            InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
            //创建会话工厂,传入MyBatis的配置文件信息
            SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
            //通过工厂得到SqlSession
            sqlSession=sqlSessionFactory.openSession();
            // 通过SqlSession操作数据库
            // 第一个参数:映射文件中statement的id,等于=namespace.statement的id
            // 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
            // sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
            // selectOne查询出一条记录
            User user=new User("FireLangX", new Date(), 1, "LangIsland");
            sqlSession.insert("test.insertSingleUser", user);
            System.out.println("############ID= "+user.getId());

            sqlSession.commit();
            // 释放资源
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

DEBUG输出的部分信息:

DEBUG [main] - ==>  Preparing: insert into user(username,birthday,sex,address) values(?,?,?,?) 
DEBUG [main] - ==> Parameters: FireLangX(String), 2017-04-04 15:56:13.383(Timestamp), 1(Integer), LangIsland(String)
DEBUG [main] - <==    Updates: 1
DEBUG [main] - ==>  Preparing: select LAST_INSERT_ID() 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 1
############ID= 32

非自增主键返回(使用uuid()):

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

执行思路:
先通过uuid()查询到主键,将主键设置到 parameterType 指定的对象中

执行uuid()语句顺序相对于insert语句之前执行。

<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
            SELECT uuid()
</selectKey>
        insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address}) 
        <!-- 注意上面的sql语句要写插入的id -->

 

删除用户:

User.xml配置文件:

    <!-- 
        删除用户:
            根据id删除用户,需要输入id值
    -->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{idx}
    </delete>

测试代码:

    @Test
    public void deleteUser(){
        SqlSession sqlSession=null;
        try {
            //以流的方式获取MyBatis的配置文件
            InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
            //创建会话工厂,传入MyBatis的配置文件信息
            SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
            //通过工厂得到SqlSession
            sqlSession=sqlSessionFactory.openSession();
            // 通过SqlSession操作数据库
            // 第一个参数:映射文件中statement的id,等于=namespace.statement的id
            // 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
            // sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
            // selectOne查询出一条记录
            sqlSession.delete("test.deleteUser", 33);

            //必须手动提交事务
            sqlSession.commit();
            // 释放资源
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

 

更新用户:

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

测试代码:

    @Test
    public void updateUser(){
        SqlSession sqlSession=null;
        try {
            //以流的方式获取MyBatis的配置文件
            InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
            //创建会话工厂,传入MyBatis的配置文件信息
            SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
            //通过工厂得到SqlSession
            sqlSession=sqlSessionFactory.openSession();
            // 通过SqlSession操作数据库
            // 第一个参数:映射文件中statement的id,等于=namespace.statement的id
            // 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
            // sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
            // selectOne查询出一条记录
            User user=new User("北斗", new Date(), 1, "FIREIsland");
            user.setId(32);
            sqlSession.update("test.updateUser", user);

            //必须手动提交事务
            sqlSession.commit();
            // 释放资源
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

整体User.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进行分类化管理,理解sql隔离 
注意:使用mapper代理方法开发,namespace有特殊重要的作用,后面会讲到
-->
<mapper namespace="test">
    <!-- 在 映射文件中配置很多sql语句 -->
    <!-- 需求:通过id查询用户表的记录 -->
    <!-- 通过 select执行数据库查询
    id:标识 映射文件中的 sql
    将sql语句封装到mappedStatement对象中,所以将id称为statement的id
    parameterType:指定输入 参数的类型,这里指定int型 
    #{}表示一个占位符号
    #{id}:其中的id表示接收输入 的参数,参数名称就是id,如果输入 参数是简单类型,#{}中的参数名可以任意,可以value或其它名称

    resultType:指定sql输出结果 的所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象。
     -->
    <select id="findUserByid" parameterType="int" resultType="cn.domarvel.entity.User">
        select * from user where id=#{id}
    </select>
    <!-- 根据用户名称模糊查询用户信息,可能返回多条
    resultType:指定就是单条记录所映射的java对象 类型
    ${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sql中。
    使用${}拼接sql,引起 sql注入
    ${value}:接收输入 参数的内容,如果传入类型是简单类型,${}中只能使用value
     -->
    <select id="findUserByLikeName" parameterType="java.lang.String" resultType="cn.domarvel.entity.User">
        select * from user where username like '%${value}%'
    </select>
    <!-- 添加用户 
    pojo是自定义类型的意思。
    parameterType:指定输入 参数类型是pojo(包括 用户信息)
    #{}中指定pojo的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值
    -->
    <insert id="insertSingleUser" parameterType="cn.domarvel.entity.User">
        <!-- 
        将插入数据的主键返回,返回到user对象中

        SELECT LAST_INSERT_ID():得到刚insert进去记录的主键值,只适用与自增主键

        keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
        order:SELECT LAST_INSERT_ID()执行顺序,相对于insert语句来说它的执行顺序
        resultType:指定SELECT LAST_INSERT_ID()的结果类型
         -->
        <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>
    <!-- 
        删除用户:
            根据id删除用户,需要输入id值
    -->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{idx}
    </delete>
    <!-- 根据id更新用户
    分析:
    需要传入用户的id
    需要传入用户的更新信息
    parameterType指定user对象,包括 id和更新信息,注意:id必须存在
    #{id}:从输入 user对象中获取id属性值
     -->
     <update id="updateUser" parameterType="cn.domarvel.entity.User">
        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
     </update>
</mapper>

总结

parameterType

  • 在映射文件中通过parameterType指定输入 参数的类型。

resultType

  • 在映射文件中通过resultType指定输出结果的类型。

#{}和${}

#{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型,pojo、hashmap。
为什么用#{}代表,而不用"?"代表占位符呢??
因为框架将sql和程序代码分离开,所以无法直接确认参数的含义,到底"?"代表哪儿个参数等等,所以框架采用了特殊的符号来代替"?"号。
如果接收简单类型,#{}中可以写成value或其它名称。
#{}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。
${}表示一个拼接符号,会引用sql注入,所以不建议使用${}。
${}接收输入参数,类型可以是简单类型,pojo、hashmap。
如果接收简单类型,${}中只能写成value。
${}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。

这里要注意一点:如果我们传递过来的参数只是一个简单类型(例如String,Interger等,并没有封装多个属性的叫做简单类型,而封装多个属性并且提供了setter和getter的叫做封装类型。Map是封装类型,取值可以根据key取值。),并且有多个#{}(名称可以任意取,如#{aa}、#{bb}),那么这里的所有#{}占位符的最终获取值都相同。

selectOne和selectList

  • selectOne表示查询出一条记录进行映射。如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)。

  • selectList表示查询出一个列表(多条记录)进行映射。如果使用selectList查询多条记录,不能使用selectOne。

如果使用selectOne报错:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值