Mybatis学习笔记(一)-环境配置-语句查询-对Mybatis如何创建出对象的思考

Mybatis学习笔记(一)-环境配置-语句查询

1.环境配置

创建一个项目后需要导入Mybaits的依赖,直接用MAVEN依赖就好

但是在POM文件中需要在build配置一个资源获取插件

<build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
</build>

要不会出现找不到配置文件的异常

2.Mybaits的代码结构

  • 实体类

  • DAO接口

  • 实现DAO接口的配置文件

  • 主配置文件

    我的目录结构

    image-20210603185649116

3.实现第一个Mybatis程序

1.编写实体类

​ 要求:

  • 实体类成员变量要和数据库中字段保持一致
  • 为成员变量实现GET/SET方法

数据库Student表

image-20210603185505526

实体类:

package MyFirstMybatisApplication.Bean;

public class StudentModle {
    private Integer id;
    private String name;
    private String email;
    private Integer age;

    public StudentModle() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student记录{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

2.编写DAO接口

​ 要求:

  • 需要使用实体类对象接收数据
package MyFirstMybatisApplication.DAO;
import MyFirstMybatisApplication.Bean.StudentModle;
public interface StudentDAO {
    StudentModle SelectStudentinfo(Integer id);
}

3.编写对应的DAO配置文件

要求:

  • 要引入DTD约束文件
  • mapper中的namespace中填写对应接口的全限命名
  • select中的id填写对应的方法名称
  • resultType中填写对应的实体类的全限命名
  • 在接口中定义的函数原型的参数用#{} 一 一对应起来
<!--#DTD约束文件-->
<?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">
<!--DAO配置-->
<mapper namespace="MyFirstMybatisApplication.DAO.StudentDAO">
    <select id="SelectStudentinfo"  resultType="MyFirstMybatisApplication.Bean.StudentModle">
        select * from student where id=#{id}
    </select>
</mapper>

4.配置主配置文件

  • environments

    在编码中可能有不同的生产环境,比如在生产中使用的数据库和测试的数据库不一样,那么我们在指定环境时候将会用到下面的标签

  • environment

    在这个标签内可以指定不同的数据库,使用id区分,那么有了数据库之后就有数据库事务方案有下面这个指定

  • transactionManager

    一般是JDBC

  • dataSource

    指定是否使用连接池 在里面property指定数据库的连接信息 那些name值是固定的

  • mappers

    用于注册其他配置文件统一管理

<!--DTD约束文件-->
<?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="development">
        <!--环境ID-->
        <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/jdbc_test?useSSL=false&amp;allowPublicKeyRetrieval=true&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--对其它的配置文件的注册-->
    <mappers>
        <mapper resource="Dao/StudentDAO.xml"/>
    </mappers>
</configuration>

5.编写实现类

方法一:获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL

注意点:

  • 使用接口类型接受sqlSession.getMapper(接口.class)
public void test1() throws IOException {
    String resource = "Mybatis.xml";
    //把配置文件信息加载到流
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //读取配置信息,创建出工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //创建出sqlSession对象
    SqlSession sqlSession对象=sqlSessionFactory.openSession();
    //获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果
    StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
    //动态代理对象
    StudentModle studentModle= studentDAO.SelectStudentinfo(2);
    System.out.println("studentModle = " + studentModle);
    sqlSession.close();
}

方法二:直接使用SqlSession,通过命名信息去执行SQL返回结果,该方式是IBatis版本留下的

注意点:

  • SqlSession的指定是接口定义的方法名
  • selectOne是SqlSession的方法,还有很多
public void test2() throws IOException {
    String resource = "Mybatis.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession=sqlSessionFactory.openSession();
    // 直接使用SqlSession,通过命名信息去执行SQL返回结果,该方式是IBatis版本留下的
    String SQLID1="MyFirstMybatisApplication.DAO.StudentDAO.SelectStudentinfo";
    StudentModle studentModle= sqlSession.selectOne(SQLID1,1);
    System.out.println("studentModle = " + studentModle);
    sqlSession.close();
}

执行结果

方法一

image-20210603204833580

方法二

image-20210603204857864

3.对其他查询结果的探究

1.多参数的查询

在StudentDAO中加入一个SelectStudentNameAndAge方法如下:

StudentModle SelectStudentNameAndAge(@Param(value = "name") String name,@Param(value = "age") Integer age);

在进行多个参数操作时需要绑定参数,使用@Param,里面的value = " "就代表了这个参数

在对应DAO配置文件中如下实现

  • select中的id填写对应的方法名称
  • resultType中填写对应的实体类的全限命名
  • 参数使用#
<select id="SelectStudentNameAndAge" resultType="MyFirstMybatisApplication.Bean.StudentModle">
        select name,age from student where name=#{name} and age=#{age}
</select>

实现类:

// 测试多个参数的查询
public void test3() throws IOException {
    String resource = "Mybatis.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession=sqlSessionFactory.openSession();
    StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
    
    StudentModle studentModle= studentDAO.SelectStudentNameAndAge("Student04",28);
    System.out.println("studentModle = " + studentModle);
    sqlSession.close();
}

结果

可以看到id email都没有赋值因为只查询了两个字段

image-20210603205603122

对多条记录的查询

在StudentDAO中加入一个SelectAllStudentinfo()方法如下:

使用了LIst集合装返回的值,指定泛型为实体类

  List<StudentModle> SelectAllStudentinfo();

对应DAO配置文件如下

<select id="SelectAllStudentinfo" resultType="MyFirstMybatisApplication.Bean.StudentModle">
    select * from student
</select>

实现类:

//获取多条数据
public void test4() throws IOException {
    String resource = "Mybatis.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession=sqlSessionFactory.openSession();
    StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
    
    List<StudentModle>FindAllinfos=studentDAO.SelectAllStudentinfo();
    for (StudentModle studentModle:FindAllinfos) {
        System.out.println(studentModle);
    }
    sqlSession.close();
}

结果:

image-20210603210033361

4.对如何创建出接口对象的思考

我们都知道接口要使用必须要有实现类,但是我并没有编写任何一个实现类,做的仅仅只是把接口对象的方法使用而已,那为什么呢?

我们先DEGUB一下看看

image-20210603210515118

1我们看到流被创建出来了

image-20210603210546869

2接着工厂被创建出来了

image-20210603210631872

3接着Sqlsession出来了

image-20210603210657554

4接着发现了一个代理对象,看到接口对象被代理了

image-20210603210723403

看见了接口对象被代理,再联想以接口为基础实现的代理技术,那就是JDK代理模式了

之前研究代理设计模式要有以下下要素

  • 原始方法
  • 实现原方法相同的接口
  • 额外方法

那我们再分析JDBC代码,不难发现获取连接的代码,释放资源的代码是冗余的,那不同的DAO方法区别就在中间的那片代码,那这个像不像所谓的额外功能?

String dburl="jdbc:mysql://localhost:3306/jdbc_test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
String dbuser="root";
String dbpassword="atgxstuSf2<e";
Class.forName("com.mysql.cj.jdbc.Driver");   
Connection conn =DriverManager.getConnection(dburl,dbuser,dbpassword);
Statement stat =conn.createStatement();
//////////////////////////////////////////////////////
String sqlString=
"select BOOKID,BOOKNAME,BOOKPRICE FROM T_book";
ResultSet rs =stat.executeQuery(sqlString);//结果集
while (rs.next()) {
String bookname =rs.getString("BOOKNAME");
String bookid =rs.getString("BOOKID");
String bookprice =rs.getString("BOOKPRICE");
System.out.println(bookid+""+bookname+""+bookprice);
   }
//////////////////////////////////////////////////////
   rs.close();
   stat.close();
   conn.close();
}
}

那么原始方法呢?

不就是获取连接的代码,释放资源的代码

再看是否实现原方法相同的接口?

参考了https://www.cnblogs.com/demingblog/p/9544774.html#mapper接口的动态代理类的生成 的源码分析

看到了如下代码

public class MapperProxyFactory<T> { //映射器代理工厂

  private final Class<T> mapperInterface;
  private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
  // 删除部分代码,便于阅读

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    //使用了JDK自带的动态代理生成映射器代理类的对象
    return (T) Proxy.newProxyInstance(否实现原方法相同的接口
             mapperInterface.getClassLoader(),//--->传入了原方法相同的接口
             new Class[] { mapperInterface }, 
             mapperProxy);
  }
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

那问题也就大致解答了

那么继续观察对应接口的配置文件

<mapper namespace="MyFirstMybatisApplication.DAO.StudentDAO">
    <select id="SelectStudentinfo"  resultType="MyFirstMybatisApplication.Bean.StudentModle">
        select * from student where id=#{id}
    </select>
</mapper>

namespace指定了接口

id指定了方法

resultType指定了结果接受的对象(可以抽象到JDBC的那个while这段取数据的操作)

还指定了SQL语句

这些信息足够生成一个原始方法了还有额外功能

这样就解释了为什么Mybaits凭什么就一个配置文件能做出许多方法来,还不带实现类的。

5.完整代码

接口:

package MyFirstMybatisApplication.DAO;

import MyFirstMybatisApplication.Bean.StudentModle;
import org.apache.ibatis.annotations.Param;
import java.util.List;

public interface StudentDAO {
    StudentModle SelectStudentinfo(Integer id);
    StudentModle SelectStudentNameAndAge(@Param(value = "name") String name,@Param(value = "age") Integer age);
    List<StudentModle> SelectAllStudentinfo();
}

对应配置文件:

<?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="MyFirstMybatisApplication.DAO.StudentDAO">
    <select id="SelectStudentinfo"  resultType="MyFirstMybatisApplication.Bean.StudentModle">
        select * from student where id=#{id}
    </select>
    <select id="SelectStudentNameAndAge" resultType="MyFirstMybatisApplication.Bean.StudentModle">
        select name,age from student where name=#{name} and age=#{age}
    </select>
    <select id="SelectAllStudentinfo" resultType="MyFirstMybatisApplication.Bean.StudentModle">
        select * from student
    </select>
</mapper>

测试类:

import MyFirstMybatisApplication.Bean.StudentModle;
import MyFirstMybatisApplication.DAO.StudentDAO;
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;
import java.util.List;

public class Test {

    @org.junit.Test
    // 测试第一个Mybatis程序
    public void test1() throws IOException {
        String resource = "Mybatis.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession=sqlSessionFactory.openSession();
        //获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果
        StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
        //动态代理
        StudentModle studentModle= studentDAO.SelectStudentinfo(2);
        System.out.println("studentModle = " + studentModle);
        sqlSession.close();
    }

    @org.junit.Test
    // 测试第一个Mybatis程序得不同实现方式
    public void test2() throws IOException {
        String resource = "Mybatis.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession=sqlSessionFactory.openSession();
        // 直接使用SqlSession,通过命名信息去执行SQL返回结果,该方式是IBatis版本留下的
        String SQLID1="MyFirstMybatisApplication.DAO.StudentDAO.SelectStudentinfo";
        StudentModle studentModle= sqlSession.selectOne(SQLID1,1);
        System.out.println("studentModle = " + studentModle);
        sqlSession.close();
    }
    @org.junit.Test
    // 测试多个参数的查询
    public void test3() throws IOException {
        String resource = "Mybatis.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession=sqlSessionFactory.openSession();
        StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
        StudentModle studentModle= studentDAO.SelectStudentNameAndAge("Student04",28);
        System.out.println("studentModle = " + studentModle);
        sqlSession.close();
    }
    @org.junit.Test
    //获取多条数据
    public void test4() throws IOException {
        String resource = "Mybatis.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession=sqlSessionFactory.openSession();
        StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
        List<StudentModle>FindAllinfos=studentDAO.SelectAllStudentinfo();
        for (StudentModle studentModle:FindAllinfos) {
            System.out.println(studentModle);
        }
        sqlSession.close();
    }
}
posted on 2021-06-03 21:32  NathenJames  阅读(139)  评论(0编辑  收藏  举报