(一)Mybatis-入门

Mybatis

背景知识

环境:

  • JDK 1.8
  • Mysql 8.0.28
  • maven 3.6.1
  • Idea

技术基础:

  • JDBC
  • mysql
  • java基础
  • maven
  • junit

SSM框架:配置文件的。最好的方式:看官网中文文档:

https://mybatis.net.cn/

一.简介

1.1 什么是mybatis

  • Mybatis是一款优秀的持久层框架

  • 它支持定制化sql、存储过程以及高级映射。

  • mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。

    • 对应到JDBC,setObject,setInt等手动设置参数不用了。

    • 对应到JDBC,获取到resultSet后,使用object.setA(resultSet.getString("a"))封装对象属性也不用了

  • Mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和JAVA的POJO(Plain Old Java Objects,普通老师java对象)为数据库中的记录。

1.2 如何获取mybatis

  • github

  • maven
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.9</version>
</dependency>

1.3 持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态化的过程
  • 内存:断点既失
  • 数据库(jdbc),io文件持久化(操作系统文件)
  • 生活:冷藏、罐头

为什么需要持久化

  • 有一些对象,不能让他丢掉。

    网站上存的钱、会员信息、密码等

  • 内存太贵了(外在原因)

1.3 持久层

Dao层、service层,controller层

  • 完成持久化工作的代码块(以前dao层就是做这个工作)
  • 层是界限十分明显的

1.4 为什么需要mybatis

  • 帮助程序员将数据存入到数据库

  • 方便

  • 传统的jdbc太复杂了,mybatis做了简化和自动化。

    如前面所说需要手动设参数、封装对象。

  • 不用mybatis也可以,但mybatis更容易上手。

  • 详细优点:

    • 简单易学,本身很小巧简单,没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较安全的掌握它的设计思路和实现。
    • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响,sql写在xml里,便于统一管理和优化,通过sql语句可以满足操作数据库的所有需求。
    • 解除sql与程序代码的耦合:通过提供Dao层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试,sql和代码的分离,提高了可维护性。(jdbc中所有的sql都在一个string里)
    • 提供映射标签,支持对象与数据库的orm字段关系映射
    • 提供对象关系映射标签,支持对象关系组件维护
    • 提供xml标签,支持编写动态sql (类似jdbc中用append实现字符串拼接sql)
  • 主流框架:Spring,SpringMVC,Mybatis

二.mybatis入门程序

2.1 搭建数据库

create database mybatis;
use mybatis

CREATE TABLE `user`(
`id` INT(20) NOT NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;


INSERT INTO USER(`id`,`name`,`pwd`) VALUES
(1,'高兴','123456'),
(2,'happy','123456'),
(3,'高兴3','123456')

2.2 创建普通maven项目

1 新建一个最普通的maven项目作为父工程

2 删除src目录,作为父工程

3 导入依赖

  • 导入mysql驱动
<!-- 导入mysql驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>
  • 导入mybatis
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.9</version>
</dependency>
  • 导入junit
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

2.3 在父工程下创建一个模块

1 编写mybatis核心配置文件

参看官方文档:一般取名核心配置文件为mybatis-config.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核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="1987518g"/>
            </dataSource>
        </environment>
        <environment id="uat">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
</configuration>

2 获取sqlSessionFactory

通过读取配置文件获取sqlSessionFactory,而通过sqlSessiionFactory获取sqlSession

3 为便利今后操作,编写工具类

package com.happy.utils;

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 java.io.IOException;
import java.io.InputStream;

//sqlSessionFactory--->sqlSession
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static{
        try {
//            使用mybatis第一步:获取sqlSessionFactory对象
            String resource="resources/mybatis-config.xml";
//            MybatisUtils.class.getClassLoader().getResourceAsStream(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//    既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
//    SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:

    public static SqlSession getSqlSession(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        sqlSession类似于jdbc的preparestatement后者statement
        return sqlSession;
    }
}

2.4 编写代码

1 编写实体类User.java

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.20</version>
    <scope>provided</scope>
</dependency>
package com.happy.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

2 Dao接口类interface UserDao

package com.happy.dao;

import com.happy.pojo.User;

import java.util.List;

public interface UserDao {
    //使用mybatis不用再像jdbc一样手动封装对象属性了,循环便利 resultSet
    //jdbc user.setUsername(resultSet.getString("username"));
    List<User> getUserlist();
}

3 编写mapper.xml文件

  • nameSpace绑定一个对应的Dao/mapper接口
  • mapper.xml可以理解为原来jdbc对接口的实现类UserDaoImpl
<?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绑定一个对应的Dao/mapper接口 -->

<mapper namespace="com.happy.dao.UserDao">
    <!--resultType 表示类型,一般用这个-->
    <!--resultMap 表示集合-->
<!--    配置内一般写类的全限定名-->
    <select id="getUserlist" resultType="com.happy.pojo.User">
        select * from mybatis.user;
    </select>
</mapper>
对比过去jdbc实现类

接口实现类由原来的UserDaoImpl转变为一个Mapper配置文件。

  • mapper.xml文件用namespace绑定接口类
  • 用id名字代表实现方法
  • 标签体专注于sql
  • 不用再写类似jdbc给prepareStatement对象传入参数setInt(1,"username")的语句,全部在mapper.xml用where id=#{id}完成
  • 不用再写类jdbbc封装对象的语句,如便利resultSet,再用resultSet.getString("username")手动封装pojo类属性

package com.happy.dao;

import com.happy.pojo.User;
import com.happy.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class UserDaoImpl implements UserDao {

//    传统的jdbc下面这些在mybatis统统不要了
    @Override
    public List<User> getUserlist() {
//        以前是需要手动实现接口,即要写sql,还便利循环并set
        String sql="select * from mybatis.user";

        Connection connection=JDBCUtils.getConnection();
        PreparedStatement preparedStatement=null;
        ResultSet resultSet=null;
        List<User> userlist= null;
        //        获取结果集,并手动封装,工作量很大
        try {
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            userlist= new ArrayList<User>();
            while(resultSet.next()){
                User user = new User();
                int id = resultSet.getInt("id");
                String name=resultSet.getString("name");
                String pwd=resultSet.getString("pwd");
                user.setId(id);
                user.setName(name);
                user.setPwd(pwd);
                userlist.add(user);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(connection,preparedStatement,resultSet);
        }
        return userlist;
    }
}

4 测试

package com.happy.dao;

import com.happy.pojo.User;
import com.happy.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDaoTest {

    @Test
    public void test() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //        执行sql
        //        方式一:getmapper,需要在核心配置文件注册mapper.xml,现获取mapper,再get方法
        UserDao mapper = sqlSession.getMapper(UserDao.class);

        System.out.println("1.使用mybatis的mapper查询--->");
        List<User> userList = mapper.getUserlist();
        for (User user : userList) {
            System.out.println(user);
        }

//        关闭sqlsession
        sqlSession.close();
    }

    @Test
    public void test2() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //        执行sql
        //        方式二:sqlSession.selectList,直接指定实现方法,已不推介使用
        List<User> userList = sqlSession.selectList("com.happy.dao.UserDao.getUserlist");

        System.out.println("2.使用mybatis的mapper的直接指定方法名的方式查询--->");
        for (User user : userList) {
            System.out.println(user);
        }

//        关闭sqlsession
        sqlSession.close();
    }


    @Test
    public void testUserDaoImpl(){
        UserDao userDao = new UserDaoImpl();
        List<User> userlist = userDao.getUserlist();
        System.out.println("3.使用原始jdbc的userDaoImpl查询--->");
        for (User user : userlist) {
            System.out.println(user);
        }
    }
}
注意1:找不到mapper文件

org.apache.ibatis.binding.BindingException: Type interface com.happy.dao.UserDao is not known to the MapperRegistry.

<!--        每一个mapper.xml都需要在mybatis核心配置文件中注册-->
<!--        <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
        <mapper resource="com/happy/dao/UserMapper.xml"></mapper>
    </mappers>
注意2:maven构建时候,部分source目录下面资源文件不能打包问题

解决方案:在build中配置resources.来防止我们资源导出失败的问题

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>
其他可能遇到的问题:
  • 配置文件没有注册mapper
  • 绑定接口错误
  • 方法名不对
  • 返回类型不对
  • maven导出资源不出

2.5 官方建议-作用域(Scope)和生命周期

理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。


提示 对象生命周期和依赖注入框架

依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器,并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。 如果对如何通过依赖注入框架使用 MyBatis 感兴趣,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。

换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭

映射器实例

映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。 也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 映射器实例并不需要被显式地关闭。尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession 的资源会让你忙不过来。 因此,最好将映射器放在方法作用域内。就像下面的例子一样:

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // 你的应用逻辑代码
}

三、CRUD

3.1 注意事项:

1 namespace

namespace中的报名要和Dao/Mapper接口的包名一致

2 注册mapper.xml

要在核心mybatis-config.xml注册对应接口mapper的mapper.xml文件。

3.2 select

选择,查询语句

3.2.1 重要参数

id:

就是对应namespace(mapper或dao接口中)中的方法名

resultType:

sql语句执行的返回类型

parameterType:

如参数类型,如果sql带参数,则需要。类似于jdbc的setInt,setString等。

3.2.2 编写代码步骤:

1 编写mapper接口(dao接口)和对应方法
package com.happy.dao;

import com.happy.pojo.User;

import java.util.List;

public interface UserMapper {
    //使用mybatis不用再像jdbc一样手动封装对象属性了,循环便利 resultSet
    //jdbc user.setUsername(resultSet.getString("username"));
    List<User> getUserlist();

    User getUserById(int id);
}

2 编写mapper.xml(类似于jdbc实现类)
<!--   resultType返回类型,配置内一般写类的全限定名-->
<!--   传入参数 id=#{id};类似于jdbc的setInt(1,id);-->
<select id="getUserById" resultType="com.happy.pojo.User" parameterType="int">
    select *
    from mybatis.user
    where id = #{id};
</select>
3 将mapper.xml注册到核心配置文件
<mappers>
    <!--        每一个mapper.xml都需要在mybatis核心配置文件中注册-->
    <!--        <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
    <mapper resource="com/happy/dao/UserMapper.xml"></mapper>
</mappers>
4 业务调用mapper接口方法

本质是动态代理,调用的实现类,可以理解为mapper.xml

 @Test
    public void testGetUserById() {
        try(SqlSession sqlSession = MybatisUtils.getSqlSession()){
            //        执行sql
            //        方式一:getmapper,需要在核心配置文件注册mapper.xml,现获取mapper,再get方法
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.getUserById(3);
            System.out.println(user);
        }
    }

3.3 insert

3.4 update

3.5 delete

3.6 步骤总结

1 编写接口

package com.happy.dao;

import com.happy.pojo.User;

import java.util.List;

public interface UserMapper {
    //使用mybatis不用再像jdbc一样手动封装对象属性了,循环便利 resultSet
    //jdbc user.setUsername(resultSet.getString("username"));
    List<User> getUserlist();

    User getUserById(int id);

    int addUser(User user);

    int updateUser(User user);

    int deleteUser(User user);
}

2 编写对应mapper中的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绑定一个对应的Dao/mapper接口 -->

<mapper namespace="com.happy.dao.UserMapper">
    <!--resultType 表示类型,一般用这个-->
    <!--resultMap 表示集合-->
    <!--    配置内一般写类的全限定名-->
    <select id="getUserlist" resultType="com.happy.pojo.User">
        select *
        from mybatis.user;
    </select>

    <!--   resultType返回类型,配置内一般写类的全限定名-->
    <!--   传入参数 id=#{id};类似于jdbc的setInt(1,id);-->
    <select id="getUserById" resultType="com.happy.pojo.User" parameterType="int">
        select *
        from mybatis.user
        where id = #{id};
    </select>

    <insert id="addUser" parameterType="com.happy.pojo.User" keyProperty="id" useGeneratedKeys="true" >
        insert into mybatis.user (id,name,pwd) values(#{id},#{name},#{pwd});
    </insert>

    <update id="updateUser" parameterType="com.happy.pojo.User">
        update mybatis.user set id=#{id},name=#{name},pwd=#{pwd}  where  id=#{id};
    </update>

    <delete id="deleteUser" parameterType="com.happy.pojo.User">
        delete from mybatis.user where id=#{id}
    </delete>
</mapper>

3 将mapper.xml注册到核心配置文件

4 测试

3.7 错误总结

  • 标签不要匹配错,insert就用insert标签,不要用select

  • resource绑定mapper,要使用路径

     <mapper resource="com/happy/dao/UserMapper.xml"></mapper>
    
  • 程序配置文件必须符合规范!

  • NullPointerException,没有注册到资源文件mybatis-config.xml!

  • 输出的xml文件中存在中文乱码问题!

  • maven资源没有导出问题,要再pom.xml文件中加build

3.8 使用万能map对象替代实体类pojo

假设,我们的实体类,或者数据库中的表,字段或者参数太多,我们可以考虑使用map,可以不用再写实体pojo类。

其实freemarker渲染模板也可以这个思路。

mapper接口

int updateUserByMap(Map user);

mapper.xml

<update id="updateUserByMap" parameterType="Map">
    update mybatis.user set id=#{id},name=#{name},pwd=#{password}  where  id=#{id};
</update>
  @Test
    public void testUpdateUserByMap() {
        try(SqlSession sqlSession = MybatisUtils.getSqlSession()){
            //        执行sql
            //        方式一:getmapper,需要在核心配置文件注册mapper.xml,现获取mapper,再get方法
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            HashMap<String, Object> userMap = new HashMap<String,Object>();
            userMap.put("id","4");
            userMap.put("name","gaogao");
            userMap.put("password","123123");
            int rows= mapper.updateUserByMap(userMap);
            if(rows>0){
                System.out.println("修改用户成功!rows为:"+rows);
            }
            sqlSession.commit();
        }
    }

3.9 传入参数parameterType总结

  • Map 传递参数,直接在sql中取出key即可!【parameterType="Map"】
  • 对象传递参数,直接在sql中取对象的属性即可!【parameterType=pojo】
  • 只有一个基本类型参数的情况下,可以直接在sql中取得!甚至可以不要parameterType 【parameterType=int】或省略
  • 多个基本类型参数,使用map或者注解。

3.10 模糊查询

两种方式

  1. java代码执行的时候
<select id="getUserByNameLike" resultType="com.happy.pojo.User" parameterType="String">
    select *
    from mybatis.user
    where name like #{name};
</select>
@Test
    public void testGetUserByNameLike() {
        try(SqlSession sqlSession = MybatisUtils.getSqlSession()){
            //        执行sql
            //        方式一:getmapper,需要在核心配置文件注册mapper.xml,现获取mapper,再get方法
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            String namelike="%"+"gao"+"%";
//            String namelike="gao";
            List<User> userList= mapper.getUserByNameLike(namelike);

            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
  1. 在sql拼接中使用通配符
    <select id="getUserByNameLike" resultType="com.happy.pojo.User" parameterType="String">
        select *
        from mybatis.user
        where name like "%"#{name}"%";
    </select>
 @Test
    public void testGetUserByNameLike() {
        try(SqlSession sqlSession = MybatisUtils.getSqlSession()){
            //        执行sql
            //        方式一:getmapper,需要在核心配置文件注册mapper.xml,现获取mapper,再get方法
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//            String namelike="'%"+"gao"+"%' or 1=1";
//            String namelike="'%zhang%' or 1=1 ";
            String namelike="gao";
            List<User> userList= mapper.getUserByNameLike(namelike);

            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
posted @ 2022-05-01 18:49  高兴518  阅读(36)  评论(0编辑  收藏  举报