欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JAVA框架——MyBatis总结(一)Mapper文件详解,动态代理方式,参数传入问题,结果返回问题

程序员文章站 2022-03-22 16:24:16
一、 Mybatis介绍它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis...

一、 Mybatis介绍

它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。

Mybatis通过xml或注解的方式将要执行的各种statement
(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

二、 使用JDBC编程的问题

1、 数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。如果使用数据库连接池可解决此问题。

2、 Sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变java代码。

3、 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。

4、 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。

三、主要类的介绍

1) Resources: mybatis中的一个类, 负责读取主配置文件

  InputStream in = Resources.getResourceAsStream("mybatis.xml");

2)SqlSessionFactoryBuilder : 创建SqlSessionFactory对象,

 SqlSessionFactoryBuilder builder  = new SqlSessionFactoryBuilder();
//创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in);

3)SqlSessionFactory : 重量级对象, 程序创建一个对象耗时比较长,使用资源比较多。 在整个项目中,有一个就够用了。

 SqlSessionFactory:接口  , 接口实现类: DefaultSqlSessionFactory
 SqlSessionFactory作用: 获取SqlSession对象。SqlSession sqlSession = factory.openSession();

openSession()方法说明:

  • openSession() :无参数的, 获取是非自动提交事务的SqlSession对象

  • openSession(boolean): openSession(true) 获取自动提交事务的SqlSession.
    openSession(false) 非自动提交事务的SqlSession对象

4)SqlSession

  • SqlSession接口 :定义了操作数据的方法
    例如 selectOne() ,selectList() ,insert(),update(), delete(), commit(), rollback()

  • SqlSession接口的实现类DefaultSqlSession。
    使用要求: SqlSession对象不是线程安全的,需要在方法内部使用, 在执行sql语句之前,使用openSession()获取SqlSession对象。

    在执行完sql语句后,需要关闭它,执行SqlSession.close(). 这样能保证他的使用是线程安全的。

5)由此引出工具类MyBatisUtils

public class MyBatisUtils {
    private static SqlSessionFactory factory =null;

    static {
        String config="mybatis.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(config);
            //创建sqlSessionFactory对象,使用SqlSessionFactoryBuilder
            factory =  new SqlSessionFactoryBuilder().build(inputStream);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获取sqlSession方法
    public static SqlSession getSqlSession(){
        SqlSession sqlSession =null;
        if (factory != null) {
            sqlSession =factory.openSession();//非自动提交事务
        }
        return sqlSession;
    }

}

四、Mapper文件

<?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="com.aiit.dao.StudentDao">
<select id="selectStudents" resultType="com.aiit.domain.Student">
        select * from student order by id;
    </select>

<!--插入操作-->
<insert id="insertStudent" >
        insert into student values (#{id},#{name},#{email},#{age})
    </insert>
</mapper>

1.mapper是当前文件的根标签

namespace:叫做命名空间,唯一值的,可以是自定义的字符串
要求使用dao接口的全限定名称

2.在当前文件中可以使用特定标签,表示数据库的特定操作

<select>,<update>,<insert>

3.数据库标签中的标签

  • id:你要执行的sql语法的唯一标识,mybatis会使用这个id的值来找到要执行的sql语句
    可以自定义,但是建议使用接口的方法名

  • resultType:表示结果类型的,是sql语句执行后得到Result,遍历这个Result得到的java对象的类型。值表示类的全限定名称

五、主配置文件

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

<!--环境配置:数据库的连接信息
        default:必须和某个environment的id值一样
        告诉mybatis使用哪个数据库的连接信息。也就是访问哪个数据库
-->
<configuration>
    <!--settings:控制mybatis全局行为-->
    <settings>
        <!--mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>


    <!--environments:一个数据库信息的配置,环境
        id:一个唯一值,自定义,表示环境的名称
    -->
    <environments default="development">
        <environment id="development">
            <!--
                transactionManager:mybatis的事务类型
                type:JDBC表示使用jdbc中的Connection中的commit和rollback事务处理
            -->
            <transactionManager type="JDBC"/>
            <!--
                dataSource,表示数据源,连接数据库的
                type:表示数据源的类型,POOLED表示使用连接池
            -->
            <!--
                也可以表示线上的数据库,是项目真是使用的库
            -->
            <dataSource type="POOLED">

                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>

    <!--
        指定sqlmapper映射文件的位置
    -->
    <mappers>
        <!--一个mapper标签指定一个文件的位置
            从类路径开始的路径信息。target/classes信息
        -->
        <mapper resource="com/aiit/dao/StudentDao.xml"/>
    </mappers>
</configuration>

<!--
        mybatis的主配置文件:主要定义了数据库的配置信息,sql映射文件的位置
        configuration
-->

六、原始Dao层开发

此处省去实体类和主配置文件

第一步:写Dao层接口

public interface StudentDao {
    List<Student> selectStudents();
}

第二步:书写mapper.xml文件

<mapper namespace="com.aiit.dao.StudentDao">
    <select id="selectStudents" resultType="com.aiit.domain.Student">
        select id,name,email,age from student order by id;
    </select>
</mapper>

第三步:书写Dao层实现类

public class StudentDaoImpl  implements StudentDao {
    @Override
    public List<Student> selectStudents() {
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        String sqlID="com.aiit.dao.studentDao.selectStudents";
        //执行sql语句,使用sqlSession类的方法
        List<Student> students = sqlSession.selectList(sqlID);
        sqlSession.close();
        return students;
    }
}

第四步:调用测试方法

public void testSelectStudents(){
        StudentDao dao =new StudentDaoImpl();
        List<Student> students = dao.selectStudents();
        students.forEach(student -> System.out.println(student));
    }
}
     List<Student> students = dao.selectStudents();
     1.dao对象,类型是StudentDao,全限定名称:com.aiit.dao.StudentDao
           这个全限定名称和mapper.xml文件中的配置是一模一样的
           
     2.方法名称,selectStudent,这个方法名称和mapper中的xml文件中查询标签的id值是一样的
    
     3.通过dao中的返回值也可以确定Mybatis要调用的sqlSession的方法
      如果返回值List,调用sqlSession,SelectList()方法
      如果返回值是int,或是非List的,看mapper文件中的标签是<insert><update>就会调用
       sqlSession的insert,update等方法

由于代码冗余,由此引出mybatis的动态代理:mybatis根据dao层的方法调用,获取执行sql语句的信息
mybatis根据你的dao接口,创建出一个dao接口实现类,并创建这个类的对象。来完成SqlSession调用方法,访问数据库。所以我们自己并不需要去书写一个dao层实现类/

七、动态代理getMapper

动态代理: 使用SqlSession.getMapper(dao接口.class) 获取这个dao接口的对象。该方法底层是使用的JDK的动态代理,自动帮我们生成Imp的实现类。而并不需要我们进行手写的方式。

Dao层接口和配置文件与上述一样

public class TestMyBatis {
    @Test
    public void testSelectStudents(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        List<Student> students = dao.selectStudents();
        students.forEach(student -> System.out.println(student));

    }
}

八、参数传入问题

1.一个简单参数

Dao 接口中方法的参数只有一个简单类型(java 基本类型和 String),占位符 #{ 任意字符 },和方法的参数名无关。

接口方法

Student selectById(Integer id);

mapper 文件

<select id="selectById" resultType="com.aiit.domain.Student">
 select * from student where id=#{id}
</select>

#{id} , id 是自定义的变量名称,和方法参数名无关

测试方法

@Test
public void testSelectById(){
//一个参数
 Student student = studentDao.selectById(1001);
 System.out.println("查询 id 是 1001 的学生:"+student);
}

2.多个参数

方式一:使用@Param

当 Dao 接口方法多个参数,需要通过名称使用参数。

在方法形参前面加入@Param(“自定义参数名”)

接口方法

 /*
    * 多个参数:命名参数,在形参定义的前面加入@param("自定义参数名称")
    * */
    List<Student> selectMulitParam(@Param("myname") String name,@Param("myage") Integer age);

mapper文件

 <select id="selectMulitParam"  resultType="com.aiit.domain.Student">
   select * from student where name=#{myname} or age=#{myage};
</select>

测试方法:

@Test
    public void testSelectMultParam(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        List<Student> students = studentDao.selectMulitParam("宋进锋", 20);
        students.forEach(student -> System.out.println(student));

        sqlSession.close(); }
}

方式二:使用JAVA对象

我们可以书写一个装饰类,里面存放的是我们需要传入的属性

public class QueryParam {
    private String paramName;
    private Integer paramAge;
}

接口方法

	/*
    * 多个参数.使用jave对象作为接口中的方法参数
    * */
    List<Student> selectMultiObject(QueryParam queryParam);

mapper.xml文件

以下由两种写法

    多个参数:使用java对象的属性值,作为参数实际值
    使用对象方法,#{属性名} javaType=类型名称,jdbcType=数据类型
    javaType:指java中属性数据类型
    jdbcType:在数据库中的数据类型
    例如:#{paramName,javaType="java.lang.String",jdbcType=VARCHAR}

    我们使用简化方式:#{属性名}  javaType jdbcType的值mybatis反射能获取。不用提供
<select id="selectMultiObject" resultType="com.aiit.domain.Student">
        select * from student where name=#{paramName,javaType="java.lang.String",jdbcType=VARCHAR}
        or #{paramAge,javaType="java.lang.Integer",jdbcType=INTEGER};
</select>
    <select id="selectMultiObject" resultType="com.aiit.domain.Student">
        select * from student where name=#{paramName}
        or #{paramAge};
    </select>

测试方法

@Test
    public void testSelectMultObject(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        QueryParam param = new QueryParam();
        param.setParamName("张三");
        param.setParamAge(22);
        List<Student> students = studentDao.selectMultiObject(param);
        students.forEach(student -> System.out.println(student));

        sqlSession.close(); }

该处一定要学会灵活运行,我们传入的对象可以是一个Student类,然而他们参数可以是Student类里面的name和age属性

方式三:按照位置传入参数(不推荐)

接口方法

/*
    *  多个参数-简单类型,按照位置传入参数
    *  mybatis 3.4之前,使用#{0} ,#{1}
    *  mybatis 3.4之后,使用#{arg0} ,#{arg1}
    * */
    List<Student> selectMultiPosition(String name,Integer age);

mapper.xml文件

 <select id="selectMultiPosition" resultType="com.aiit.domain.Student">
        select * from student where name=#{arg0}
        or #{arg1};
    </select>

方式四:使用map集合传入参数(不推荐)

接口方法

  /*
    *  多个参数,使用Map存放多个值
    * */
    List<Student> selectMulitByMap(Map<String, Object>map);

测试方法

@Test
    public void testSelectMultiByMap(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);

        Map<String, Object> data = new HashMap<>();
        data.put("myname","张三");
        data.put("myage",28);
        List<Student> students = studentDao.selectMulitByMap(data);
        students.forEach(student -> System.out.println(student));

        sqlSession.close(); }
}

mapper.xml配置文件

<select id="selectMultiPosition" resultType="com.aiit.domain.Student">
    select * from student where name=#{myname}
    or #{myage};
</select>

3.# 和 $

1.使用#,可以替换成?

  select id,name, email,age from student where id=#{id}

#的结果: select id,name, email,age from student where id=?

2.使用$,可以替换成字符串拼接

select id,name, email,age from student where id=${studentId}

$ 的结果:select id,name, email,age from student where id=1001
也可以写成如下:

String sql="select id,name, email,age from student where id=" + "1001";

# 和 $区别

  1. #使用 ?在sql语句中占位的, 使用PreparedStatement执行sql,效率高

  2. #能够避免sql注入,更安全。

  3. $不使用占位符,是字符串连接方式,使用Statement对象执行sql,效率低

  4. $有sql注入的风险,缺乏安全性。

  5. $:可以替换表名或者列名(select * from user order by name)这里的name可以进行值得更改

举例SQL注入问题

如果我们参数传过来的值为:

List<Student> students = dao.selectUser("'李四';drop table student");

因为$是一种字符串的拼接方式,那么当我们传入这种参数的时候,会发生两个语句

select * from student where name ='李四';drop table student;

此时会导致我们的数据库里面删除,用户在输入这些语句的时候,会造成我们的数据库的隐患。

九、结果返回问题

1、resultType结果类型

  • 指sql语句执行完毕后, 数据转为的java对象, java类型是任意的。

  • resultType结果类型的它值

    • 类型的全限定名称
    • 类型的别名, 例如 java.lang.Integer别名是int
    • 当我们返回值是集合,只要返回的值是< T >当中的泛型即可

案例

接口文件

 List<Student> selectMultiParam(@Param("myname") String name,@Param("myage") Integer age);

Integer queryCountStudent();

Mapper.xml文件

 <select id="selectMultiParam"  resultType="com.aiit.domain.Student">
       select * from student;
</select>
 <select id="queryCountStudent"  resultType="int">
       select count(*) from student;
</select>

2、 定义自定义类型的别名

在mybatis主配置文件中定义,使<typeAlias>定义别名

  • 可以在resultType中使用自定义别名
  • 也可以使用package标签

主配置文件

<typeAliases>
        <!--方式一-->
        <typeAlias type="com.aiit.domain.Student" alias="Student"></typeAlias>
        <!--方式二-->
        <package name="com.aiit.domain"/>
    </typeAliases>

mapper文件

 <select id="selectStudentById"  resultType="Student">
       select * from student where id=#{id};
    </select>

推荐使用全包名的方式,不适用取别名的方式,因为如果我们使用了相同类名的属性,会进行报错

本文地址:https://blog.csdn.net/Mr_GYF/article/details/110881771