MyBatis知识点

MyBatis

三层架构

  • MyBatis 框架

​ MyBatis 是一个优秀的基于 java 的持久层框架,内部封装了 jdbc,开发者只需要关注 sql 语句本身,而不需要处理加载驱动、创建连接、创建 statement、关闭连接,资源等繁杂的过程。 MyBatis 通过 xml 或注解两种方式将要执行的各种 sql 语句配置起来, 并通过 java 对象和 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java
对象并返回。

  • Spring 框架

​ Spring 框架为了解决软件开发的复杂性而创建的。 Spring 使用的是基本的 JavaBean 来完成以前非常复杂的企业级开发。 Spring 解决了业务对象, 功能模块之间的耦合, 不仅在 javase,web 中使用,大部分 Java 应用都可以从 Spring 中受益。
​ Spring 是一个轻量级控制反转(IoC)和面向切面(AOP)的容器。

  • SpringMVC框架

​ Spring MVC 属于 SpringFrameWork 3.0 版本加入的一个模块,为 Spring 框架提供了构建 Web 应用程序的能力。现在可以 Spring 框架提供的 SpringMVC 模块实现 web 应用开发,在 web 项目中可以无缝使用 Spring 和 Spring MVC 框架。

JDBC回顾

编程实现

  • 查询数据代码实现
public void findStudent() { 
    Connection conn = null; 
    Statement stmt = null; 
    ResultSet rs = null; 
    try { 
        //注册 mysql 驱动 
        Class.forName("com.mysql.jdbc.Driver"); 
        //连接数据的基本信息 url,username,password 
        String url = "jdbc:mysql://localhost:3306/springdb"; 
        String username = "root"; 
        String password = ""; 
        //创建连接对象 
        conn = DriverManager.getConnection(url, username, password); 
        //保存查询结果 
        List<Student> stuList = new ArrayList<>(); 
        //创建 Statement, 用来执行 sql 语句 
        stmt = conn.createStatement(); 
        //执行查询,创建记录集, 
        rs = stmt.executeQuery("select * from student"); 
        while (rs.next()) { 
            Student stu = new Student(); 
            stu.setId(rs.getInt("id")); 
            stu.setName(rs.getString("name")); 
            stu.setAge(rs.getInt("age")); 
            //从数据库取出数据转为 Student 对象,封装到 List 集合 
            stuList.add(stu); 
        } 
    } catch (Exception e) { 
        e.printStackTrace(); 
    } finally { 
        try { 
            //关闭资源 
            if (rs != null) ; 
            { 
                rs.close(); 
            } 
            if (stmt != null) { 
                stmt.close(); 
            } 
            if (conn != null) { 
                conn.close(); 
            } 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 
}
  • 使用JDBC的缺陷
    • 代码比较多,开发效率低
    • 需要关注Connection,Statement,ResultSet对象的创建和销毁
    • 对ResultSet查询的结果,需要自己手动封装成为List
    • 重复的代码比较多
    • 业务代码和数据库操作的代码混在一起,耦合度比较高

MyBatis

介绍

​ MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。2013 年 11 月迁移到 Github。 iBATIS 一词来源于“internet”和“abatis”的组合,是一个基于 Java 的持久层框架。iBATIS 提供的持久层框架包括 SQL Maps 和 Data Access Objects(DAOs)

JDBC解决的问题:

减轻使用 JDBC 的复杂性,不用编写重复的创建 Connetion , Statement ; 不用编写关闭资源代码。 直接使用 java 对象,表示结果数据。让开发者专注 SQL 的处理。 其他分心的工作由 MyBatis 代劳。

MyBatis 中编写的xml代码可以等价类比于JDBC编写的代码:

  1. 注册数据库的驱动,例如

    Class.forName(com.mysql.jdbc.Driver)
    
  2. 创建 JDBC 中必须使用的 Connection , Statement, ResultSet 对象

  3. 从 xml 中获取 sql,并执行 sql 语句,把 ResultSet 结果转换 java 对象

    List<Student> list = new ArrayLsit<>(); 
    ResultSet rs = state.executeQuery(“select * from student”); 
    while(rs.next){ 
        Student student = new Student(); 
        student.setName(rs.getString(“name”)); 
        student.setAge(rs.getInt(“age”)); 
        list.add(student); 
    } 
    
  4. 关闭资源

     ResultSet.close() , Statement.close() , Conenection.close()
    

入门案例

  • 创建student表
CREATE TABLE `student` ( 
    `id` int(11) NOT NULL , 
    `name` varchar(255) DEFAULT NULL, 
    `email` varchar(255) DEFAULT NULL, 
    `age` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
  • 依赖
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.1</version>
</dependency>
# 数据库驱动
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.9</version>
</dependency>
  • maven插件
<build> 
    <resources> 
        <resource> 
            <!--所在的目录-->
            <directory>src/main/java</directory> 
            <includes>
                <!--包括目录下的.properties,.xml 文件都会扫描到--> 
                <include>**/*.properties</include> 
                <include>**/*.xml</include> 
            </includes> 
            <filtering>false</filtering> 
        </resource> 
    </resources> 

    <plugins> 
        <plugin> 
            <artifactId>maven-compiler-plugin</artifactId> 
            <version>3.1</version> 
            <configuration> 
                <source>1.8</source> 
                <target>1.8</target> 
            </configuration> 
        </plugin> 
    </plugins> 
</build> 
  • StudentDao.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 :必须有值,自定义的唯一字符串,推荐使用:dao 接口的全限定名称,就是要操作的是哪张表对应的接口  
        --> 
		<mapper namespace="com.liu.dao.StudentDao"> 
            
       <!-- 
       <select>:  查询数据,   标签中必须是 select 语句 
              id:  sql  语句的自定义名称,推荐使用 dao接口中方法名称,使用名称表示要执行的 sql语句 
     	resultType:  查询语句的返回结果数据类型,使用全限定类名    
        --> 
    <select id="selectStudents" resultType="com.liu.domain.Student"> 
        <!-- 要执行的 sql  语句--> 
        select id,name,email,age from student 
    </select> 
</mapper>
  • Mybatis-config.xml

    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> 
    <!-- 配置 mybatis  环境--> 
    <environments default="mysql"> 
        <!--id: 数据源的名称--> 
        <environment id="mysql"> 
            <!-- 配置事务类型:使用 JDBC  事务(使用 Connection  的提交和回滚)--> 
            <transactionManager type="JDBC"/> 
            <!-- 数据源 dataSource :创建数据库 Connection 对象 
                type: POOLED  使用数据库的连接池  --> 
            <dataSource type="POOLED"> 
                <!-- 连接数据库的四个要素--> 
                <property name="driver" value="com.mysql.jdbc.Driver"/> 
                <property name="url" value="jdbc:mysql://localhost:3306/ssm"/> 
                <property name="username" value="root"/> 
                <property name="password" value=""/> 
            </dataSource> 
        </environment> 
    </environments> 
    <mappers> 
        <!-- 告诉 mybatis要执行的sql语句的位置,如果有多个xxxDao.xml文件就要编写多个--> 
        <mapper resource="com/liu/dao/StudentDao.xml"/> 
    </mappers> 
</configuration>
# 支持中文的 url 
jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8
  • 测试
@Test 
public void testStart() throws IOException { 
    //1.mybatis  主配置文件  
    String config = "mybatis-config.xml"; 
    //2. 读取配置文件  
    InputStream in = Resources.getResourceAsStream(config); 
    //3. 创建 SqlSessionFactory(工厂)对象,然后从factory里面获取对象,目的是获取 SqlSession  
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); 
    //4. 获取 SqlSession,SqlSession能执行 sql  语句 
    SqlSession session = factory.openSession(); 
    //5. 执行 SqlSession  的 selectList() 
    List<Student> studentList = session.selectList("com.liu.dao.StudentDao.selectStudents"); 
    //6. 循环输出查询结果  
    studentList.forEach( student -> System.out.println(student)); 
    //7. 关闭 SqlSession ,释放资源 
    session.close(); 
} 
  • 类似等价于之前的JDBC部分代码
List<Student> studentList = session.selectList("com.liu.dao.StudentDao.selectStudents"); // 近似等价的 jdbc 代码 
Connection conn = 获取连接对象 
String sql=”select id,name,email,age from student” 
PreparedStatement ps = conn.prepareStatement(sql); 
ResultSet rs = ps.executeQuery(); 
// 封装对象......
  • 配置日志功能
<!-- mybatis.xml 文件加入日志配置,可以在控制台输出执行的 sql 语句和参数 -->
<settings> 
    <setting name="logImpl" value="STDOUT_LOGGING" /> 
</settings> 

CRUD

insert
  • StudentDao.xml
<insert id="insertStudent"> 
    insert into student(id,name,email,age) 
    values(#{id},#{name},#{email},#{age}) 
</insert> 
  • 测试
@Test 
public void testInsert() throws IOException { 
    //1.mybatis  主配置文件  
    String config = "mybatis-config.xml"; 
    //2. 读取配置文件  
    InputStream in = Resources.getResourceAsStream(config); 
    //3. 创建 SqlSessionFactory  对象  
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); 
    //4. 获取 SqlSession 
    SqlSession sqlSession = factory.openSession(); 
    
    //5. 创建保存数据的对象  
    Student student = new Student(); 
    student.setId(1005); 
    student.setName(" 张丽"); 
    student.setEmail("zhangli@163.com"); 
    student.setAge(20); 
    
    //6. 执行插入 insert,返回值是受影响的行数
    int rows =  sqlSession.insert("com.liu.dao.StudentDao.insertStudent",student); 
    //7. 提交事务  
    sqlSession.commit(); 
    System.out.println(" 增加记录的行数:"+rows); 
    //8. 关闭 SqlSession 
    sqlSession.close(); 
}
update
  • StudentDao.xml
<update id="updateStudent"> 
    update student set age = #{age} where id=#{id} 
</update>
  • 测试
//5. 创建保存数据的对象  
Student student = new Student(); 
student.setId(1005);// 要修改的 id 
student.setAge(30); // 要修改的年龄值  
//6. 执行 更新 update 
int rows = session.update("com.liu.dao.StudentDao.updateStudent",student); 
//7. 提交事务----
delete
  • StudentDao.xml
<delete id="deleteStudent"> 
    delete from student where id=#{studentId} 
</delete>
  • 测试
//5. 删除的 id 
int id = 1001; 
//6. 执行删除 delete 
int rows = session.delete("com.liu.dao.StudentDao.deleteStudent",id); 
//7. 提交事务 

Mybatis对象分析

Resources 类
Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。

SqlSessionFactoryBuilder 类
SqlSessionFactory 的创建,需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方法。由于SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。

SqlSessionFactory 接口
SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用只需要一个该对象即可。创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。
➢ openSession(true):创建一个有自动提交功能的 SqlSession
➢ openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
➢ openSession():同 openSession(false) ,默认是使用手动提交事务

SqlSession 接口
SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭。

工具类

  • 创建MyBatisUtil.java
public class MyBatisUtil { 
    // 定义 SqlSessionFactory 
    private static SqlSessionFactory factory = null; 
    static { 
        // 使用   静态块   创建一次 SqlSessionFactory 
        try{ 
            String config = "mybatis-config.xml"; 
            // 读取配置文件  
            InputStream in = Resources.getResourceAsStream(config); 
            // 创建 SqlSessionFactory  对象  
            factory = new SqlSessionFactoryBuilder().build(in); 
        }catch (Exception e){ 
            factory = null; 
            e.printStackTrace(); 
        } 
    } 

    /*  获取 SqlSession  对象 */ 
    public static SqlSession getSqlSession(){ 
        SqlSession session = null; 
        if( factory != null){ 
            // 从factory中获取sqlSession对象,默认需要手动提交
            session = factory.openSession(); 
        } 
        return session; 
    } 
} 
  • 测试
@Test 
public void testUtils() throws IOException { 
    // 获取sqlSession对象
    SqlSession session = MyBatisUtil.getSqlSession(); 
    // 执行查询方法
    List<Student> studentList =  session.selectList("com.liu.dao.StudentDao.selectStudents"); 
    studentList.forEach( student -> System.out.println(student)); 
    session.close(); 
} 

CRUD-Plus

getMapper 获取代理对象
  • 只需调用 SqlSession 的 getMapper()方法, 即可获取指定接口的实现类对象。 该方法的参数为指定 Dao接口类的 class 值

    SqlSession session = factory.openSession(); 
    StudentDao dao = session.getMapper(StudentDao.class); 
    
  • 使用工具类:

    StudentDao studentDao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class); 
    
代理对象执行CRUD
@Test 
public void testSelect() throws IOException { 
    final List<Student> studentList = studentDao.selectStudents(); 
    studentList.forEach( stu -> System.out.println(stu)); 
} 
@Test 
public void testInsert() throws IOException { 
    Student student = new Student(); 
    student.setId(1006); 
    student.setName(" 林浩"); 
    student.setEmail("linhao@163.com"); 
    student.setAge(26); 
    int nums = studentDao.insertStudent(student); 
    System.out.println(" 使用Dao添加数据:"+nums); 
} 
@Test 
public void testUpdate() throws IOException { 
    Student student = new Student(); 
    student.setId(1006); 
    student.setAge(28); 
    int nums = studentDao.updateStudent(student); 
    System.out.println(" 使用Dao修改数据:"+nums); 
}
@Test 
public void testDelete() throws IOException { 
    int nums = studentDao.deleteStudent(1006); 
    System.out.println("使用Dao修改数据:"+nums); 
}
xxxMapper.xml参数解析

xxxDao.xml和xxxMapper.xml指的是同一个文件

parameterType:

​ 接口中方法参数的类型, 类型的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以推断出具体传入语句的参数,默认值为未设置(unset) 。接口中方法的参数从 java 代码传入到mapper 文件的 sql 语句。

int / java.lang.Integer 
HashMap / java.util.HashMap 
List / java.util.ArrayList 
Student / com.liu.domain.Student
 
// <select>,<insert>,<update>,<delete>都可以使用 parameterType 指定类型
<delete id="deleteStudent" parameterType="int"> 
    delete from student where id=#{studentId} 
</delete> 

// 
    
<delete id="deleteStudent" parameterType="java.lang.Integer"> 
    delete from student where id=#{studentId} 
</delete>
参数传递
  • 1 个参数

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

// 接口方法: 
Student selectById(int id); 

// Mapper.xml 文件: 
<select id="selectById" resultType="com.liu.domain.Student"> 
    select id,name,email,age from student where id=#{studentId} 
</select> 
// {studentId} , studentId 是自定义的变量名称,和方法参数名无关。等价于把id的值赋给StudentId
    
@Test 
public void testSelectById(){ 
    // 一个参数  
    Student student = studentDao.selectById(1005); 
    System.out.println(" 查询id是1005的学生:"+student); 
}
  • 多个参数- 使用@Param

    当 Dao 接口方法多个参数, 需要通过名称使用参数。 在方法形参前面加入@Param(“自定义参数名”),mapper 文件使用#{自定义参数名}。

// 接口方法: 
List<Student> selectMultiParam(@Param("personName") String name, 
                                     @Param("personAge") int age); 
 
// mapper 文件: 因为上面使用了@param指定了name的别名是personName,所以在xml中的部分也得使用personName这个名字
<select id="selectMultiParam" resultType="com.bjpowernode.domain.Student"> 
    select id,name,email,age from student where name=#{personName} or age=#{personAge} 
</select> 
 
// 测试方法: 
@Test 
public void testSelectMultiParam(){ 
    List<Student> stuList = studentDao.selectMultiParam(" 李力",20); 
    stuList.forEach( stu -> System.out.println(stu)); 
}
  • 多个参数- 使用对象

    使用 java 对象传递参数, java 的属性值就是 sql 需要的参数值。 每一个属性就是一个参数。 语法格式: #{property,javaType=java 中数据类型名,jdbcType=数据类型名称 } ,javaType, jdbcType 的类型 MyBatis 可以检测出来,一般不需要设置。常用格式 #{ property }。

    使用对象进行传递参数是我们在真实的项目开发中采用的方法

    // 设置对象进行封装需要查询的字段信息
    public class QueryParam { 
        private String queryName; 
        private int queryAge; 
        //set  ,get  方法 
    } 
    
    
    // 接口方法: 
    List<Student> selectMultiObject(QueryParam queryParam); 
     
    // mapper 文件: 
    <select id="selectMultiObject" resultType="com.liu.domain.Student"> 
        select id,name,email,age from student where name=#{queryName} or age=#{queryAge} 
    </select> 
        
    // 或 
        
    <select id="selectMultiObject" resultType="com.liu.domain.Student"> 
        select id,name,email,age from student 
        where name=#{queryName,javaType=string,jdbcType=VARCHAR} 
        or age =#{queryAge,javaType=int,jdbcType=INTEGER} 
    </select> 
     
    // 测试方法: 
    @Test 
    public void selectMultiObject(){ 
        QueryParam qp  = new QueryParam(); 
        qp.setQueryName("李力"); 
        qp.setQueryAge(20); 
        List<Student> stuList = studentDao.selectMultiObject(qp); 
        stuList.forEach( stu -> System.out.println(stu)); 
    } 
    
  • 多个参数- 按位置

参数位置从 0 开始, 引用参数语法 #{ arg 位置 } , 第一个参数是#{arg0}, 第二个是#{arg1}。

// 接口方法: 
List<Student> selectByNameAndAge(String name,int age); 
 
// mapper 文件 
<select id="selectByNameAndAge" resultType="com.liu.domain.Student"> 
    select id,name,email,age from student where name=#{arg0} or age =#{arg1} 
</select> 
 
// 测试方法: 
@Test 
public void testSelectByNameAndAge(){ 
    // 按位置参数  
   List<Student> stuList = studentDao.selectByNameAndAge(" 李力",20); 
   stuList.forEach( stu -> System.out.println(stu)); 
}
  • 多个参数- 使用 Map

    Map集合可以存储多个值, 使用Map向mapper文件一次传入多个参数。 Map集合使用String的key, Object 类型的值存储参数。 mapper 文件使用 # { key } 引用参数值。

    // 例如:
    Map<String,Object> data = new HashMap<String,Object>(); 
    data.put(“myname”,”李力”); 
    data.put(“myage”,20); 
     
    // 接口方法: 
    List<Student> selectMultiMap(Map<String,Object> map); 
     
    // mapper 文件: 
    <select id="selectMultiMap" resultType="com.liu.domain.Student"> 
        select id,name,email,age from student where name=#{myname} or age =#{myage} 
    </select> 
     
    // 测试方法: 
    @Test 
    public void testSelectMultiMap(){ 
        Map<String,Object> data = new HashMap<>(); 
        data.put("myname"," 李力");// #{myname} 
        data.put("myage",20);    // #{myage} 
        List<Student> stuList = studentDao.selectMultiMap(data); 
        stuList.forEach( stu -> System.out.println(stu)); 
    } 
    
  • # 和$

    ‘#’ 占位符,告诉 mybatis 使用实际的参数值代替。并使用 PrepareStatement 对象执行 sql 语句, #{…}代替
    sql 语句的“?” 。这样做更安全,更迅速,通常也是首选做法。

    // mapper 文件 
    <select id="selectById" resultType="com.liu.domain.Student"> 
        select id,name,email,age from student where id=#{studentId} 
    </select> 
     
    // 转为 MyBatis 的执行是: 
    String sql=” select id,name,email,age from student where id=?; 
    PreparedStatement ps = conn.prepareStatement(sql); 
    ps.setInt(1,1005); 
     
    // 解释: 
    // where id=? 就是 where id=#{studentId}  
    // ps.setInt(1,1005) , 1005 会替换掉 #{studentId} 
     
    // $ 字符串替换,告诉mybatis使用 $ 包含的“字符串”替换所在位置。使用 Statement 把 sql 语句和${}的
    // 内容连接起来。主要用在替换表名,列名,不同列排序等操作。 
     
    // 例 1: 分别使用 id , email 列查询 Student 
    // 接口方法: 
    Student findById(int id); 
    Student findByEmail(String email); 
     
    // mapper 文件: 
    <select id="findById" resultType="com.liu.domain.Student"> 
          select * from student where id=#{studentId} 
    </select> 
     
    <select id="findByEmail" resultType="com.liu.domain.Student"> 
          select * from student where email=#{stuentEmail} 
    </select> 
     
    // 测试方法: 
    @Test 
    public void testFindStuent(){ 
        Student student1 = studentDao.findById(1002); 
        System.out.println("findById:"+student1); 
     
        Student student2 = studentDao.findByEmail("zhou@126.net"); 
        System.out.println("findByEmail:"+student2); 
    } 
     
    // 例 2:通用方法,使用不同列作为查询条件 
    // 接口方法: 
    Student findByDiffField(@Param("col") String colunName,@Param("cval") Object value); 
     
    // mapper 文件: 
    <select id="findByDiffField"  resultType="com.liu.domain.Student"> 
         select * from student where ${col} = #{cval} 
    </select> 
     
    测试方法: 
    @Test 
    public void testFindDiffField(){ 
        Student student1 = studentDao.findByDiffField("id",1002); 
        System.out.println(" 按 id  列查询:"+student1); 
     
        Student student2 = studentDao.findByDiffField("email","zhou@126.net"); 
        System.out.println(" 按 email  列查询:"+student2); 
    } 
    
封装 MyBatis 输出结果
  • resultType

    ​ 执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。resultType 和 resultMap,不能同时使用。

  • 简单类型

    // 接口方法: 
    public int countStudent(); 
     
    // mapper 文件: 
    <select id="countStudent" resultType="int"> 
        select count(*) from student 
    </select> 
     
    // 测试方法: 
    @Test 
    public void testRetunInt(){ 
        int count = studentDao.countStudent(); 
        System.out.println(" 学生总人数:"+ count); 
    } 
    
  • 对象类型

    // 接口方法: 
    public Student selectById(int id); 
     
    // mapper 文件: 
    <select id="selectById" resultType="com.liu.domain.Student"> 
        select id,name,email,age from student where id=#{id} 
    </select>
    
    • 注意:Dao 接口方法返回是集合类型,需要指定集合中的类型,不是集合本身。
  • Map

    sql 的查询结果作为 Map 的 key 和 value。推荐使用 Map<Object,Object>。 注意:Map 作为接口返回值,sql 语句的查询结果最多只能有一条记录。大于一条记录是错误。

    // 接口方法: 
    Map<Object,Object> selectReturnMap(int id); 
     
    // mapper 文件: 
    <select id="selectReturnMap" resultType="java.util.HashMap"> 
        select name,email from student where id = #{studentId} 
    </select> 
     
    // 测试方法: 
    @Test 
    public void testReturnMap(){ 
        Map<Object,Object> retMap = studentDao.selectReturnMap(1002); 
        System.out.println(" 查询结果是 Map:"+retMap); 
    } 
    
  • ResultMap

    resultMap 可以自定义 sql 的结果和 java 对象属性的映射关系。更灵活的把列值赋值给指定属性。 常用在列名和 java 对象属性名不一样的情况。

    使用方式:
    1.先定义 resultMap,指定列名和属性的对应关系。
    2.在中把 resultType 替换为 resultMap。

    // 接口方法: 
    List<Student> selectUseResultMap(QueryParam param); 
     
    // mapper 文件: 
    <!--  
         自定义创建 ResultMap类型的返回值 
         id: 自定义的唯一名称,在<select> 使用 
         type: 期望转为的 java  对象的全限定名称或别名        
    -->      
    <resultMap id="studentMap" type="com.liu.domain.Student"> 
        <!--  主键字段使用 id --> 
        <id column="id" property="id" /> 
        <!-- 非主键字段使用 result--> 
        <result column="name" property="name"/> 
        <result column="email" property="email" /> 
        <result column="age" property="age" /> 
    </resultMap> 
     
    <!--resultMap: resultMap  标签中的 id  属性值--> 
    <select id="selectUseResultMap" resultMap="studentMap"> 
        select id,name,email,age from student where name=#{queryName} or age=#{queryAge} 
    </select> 
     
    // 测试方法: 
    @Test 
    public void testSelectUseResultMap(){ 
        QueryParam param = new QueryParam(); 
        param.setQueryName(" 李力"); 
        param.setQueryAge(20); 
        List<Student> stuList = studentDao.selectUseResultMap(param); 
        stuList.forEach( stu -> System.out.println(stu)); 
    }
    
实体类属性名和列名不同 的 处理方式
  • 使用列别名 和

    // 查询需要返回的对象类型
    public class PrimaryStudent { 
        private Integer stuId; 
        private String stuName; 
        private Integer stuAge; 
        // set , get  方法 
    } 
    
    // 2. 接口方法 
    List<PrimaryStudent> selectUseFieldAlias(QueryParam param); 
     
    // 3. mapper 文件: 
    <select id="selectUseFieldAlias" 
    resultType="com.liu.domain.PrimaryStudent"> 
        select id as stuId, name as stuName,age as stuAge 
        from student where  name=#{queryName} or age=#{queryAge} 
    </select> 
     
    // 4.测试方法 
    @Test 
    public void testSelectUseFieldAlias(){ 
        QueryParam param = new QueryParam(); 
        param.setQueryName(" 李力"); 
        param.setQueryAge(20); 
        List<PrimaryStudent> stuList = studentDao.selectUseFieldAlias(param); 
        stuList.forEach( stu -> System.out.println(stu)); 
    } 
    
  • 使用

    // 1. 接口方法 
    List<PrimaryStudent> selectUseDiffResultMap(QueryParam param); 
     
    // 2. mapper 文件: 
    <!--  
        自定义创建返回类型的 resultMap 
       id: 自定义的唯一名称,在<select> 使用 
       type: 期望转为的 java  对象的全限定名称或别名  
    --> 
    <resultMap id="primaryStudentMap" 
    type="com.liu.domain.PrimaryStudent"> 
        <!--  主键字段使用id,使用字段映射的方式,这样子就不需要在sql语句中进行重命名 --> 
        <id column="id" property="stuId" /> 
        <!-- 非主键字段使用 result--> 
        <result column="name" property="stuName"/> 
        <result column="age" property="stuAge" /> 
    </resultMap> 
     
    <!--resultMap: resultMap  标签中的 id  属性值--> 
    <select id="selectUseDiffResultMap" resultMap="primaryStudentMap"> 
        select id,name,email,age from student 
        where name=#{queryName} or age=#{queryAge} 
    </select> 
     
    // 3. 测试方法 
    @Test 
    public void testSelectUseDiffResultMap(){ 
        QueryParam param = new QueryParam(); 
        param.setQueryName(" 李力"); 
        param.setQueryAge(20); 
        List<PrimaryStudent> stuList; 
        stuList = studentDao.selectUseDiffResultMap(param); 
        stuList.forEach( stu -> System.out.println(stu)); 
    } 
    
  • 模糊 like

    模糊查询的实现有两种方式, 一是 java 代码中给查询数据加上“%” ; 二是在 mapper 文件 sql 语句的条件位置加上“%”。

    // 需求:查询姓名有“力”的 
     
    // 例 1: java  代码中提供要查询的  “% 力% ” 
    // 接口方法: 
    List<Student> selectLikeFirst(String name); 
     
    // mapper 文件: 
    <select id="selectLikeFirst" resultType="com.liu.domain.Student"> 
        select id,name,email,age from student 
        where name like #{studentName} 
    </select> 
     
    // 测试方法: 
    @Test 
    public void testSelectLikeOne(){ 
        String name="%力%"; 
        List<Student> stuList = studentDao.selectLikeFirst(name); 
        stuList.forEach( stu -> System.out.println(stu)); 
    } 
     
    ========================================
        
    // 例 2:mapper 文件中使用 like name "%" #{xxx} "%" 
    // 接口方法: 
    List<Student> selectLikeSecond(String name); 
     
    mapper 文件: 
    <select id="selectLikeSecond" resultType="com.liu.domain.Student"> 
        select id,name,email,age from student 
        where name like "%'#{studentName}'%" 
    </select> 
     
    // 测试方法: 
    @Test 
    public void testSelectLikeSecond(){ 
        String name=" 力"; 
        List<Student> stuList = studentDao.selectLikeSecond(name); 
        stuList.forEach( stu -> System.out.println(stu)); 
    } 
    

MyBatis 框架动态 SQL

​ 动态 SQL,通过 MyBatis 提供的各种标签对条件作出判断以实现动态拼接 SQL 语句。这里的条件判断使用的表达式为 OGNL 表达式。常用的动态 SQL 标签有、、、等。 MyBatis 的动态 SQL 语句,与 JSTL 中的语句非常相似。 动态 SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的 SQL 语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的 SQL 语句。此时,可使用动态 SQL 来解决这样的问题。

  • 动态 SQL 之

    对于该标签的执行,当 test 的值为 true 时,会将其包含的 SQL 片断拼接到其所在的 SQL 语句中。 语法: sql 语句的部分

    // 接口方法: 
    List<Student> selectStudentIf(Student student);  
     
    // mapper 文件: 
    <select id="selectStudentIf" resultType="com.liu.domain.Student"> 
        select id,name,email,age from student 
        where 1=1 
    	<!--动态sql语句的if判断语句的实现-->    
        <if test="name != null and name !='' "> 
            and name = #{name} 
        </if> 
        <if test="age > 0 "> 
            and age &gt; #{age} 
        </if> 
    </select> 
     
    // 测试方法: 
    @Test 
    public void testSelect() throws IOException { 
        Student param = new Student(); 
        param.setName(" 李力"); 
        param.setAge(18); 
     
        List<Student> studentList = studentDao.selectStudentIf(param); 
        studentList.forEach( stu -> System.out.println(stu)); 
    } 
    
  • 动态 SQL 之

    ​ 标签的中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1 的子句。因为,若 where 后的所有条件均为 false,而 where 后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 where,SQL出错。所以,在 where 后,需要添加永为真子句 1=1,以防止这种情况的发生。但当数据量很大时,会严重影响查询效率。
    ​ 使用标签,在有查询条件时,可以自动添加上 where 子句;没有查询条件时,不会添加where 子句。需要注意的是,第一个标签中的 SQL 片断,可以不包含 and。不过,写上 and 也不错,系统会将多出的 and 去掉。但其中 SQL 片断的 and,必须要求写上。否则 SQL 语句将拼接出错 。
    ​ 语法: 其他动态 sql

    // 接口方法: 
    List<Student> selectStudentWhere(Student student); 
     
    // mapper 文件: 
    <select id="selectStudentWhere" resultType="com.liu.domain.Student"> 
        select id,name,email,age from student
        <where> 
            <if test="name != null and name !='' "> 
                and name = #{name} 
            </if> 
            <if test="age > 0 "> 
                and age &gt; #{age} 
            </if> 
        </where> 
    </select> 
     
    // 测试方法: 
    @Test 
    public void testSelectWhere() throws IOException { 
        Student param = new Student(); 
        param.setName(" 李力"); 
        param.setAge(18); 
     
        List<Student> studentList = studentDao.selectStudentWhere(param); 
        studentList.forEach( stu -> System.out.println(stu)); 
    }
    
  • 动态 SQL 之

    标签用于实现对于数组与集合的遍历。对其使用,需要注意:
    ➢ collection 表示要遍历的集合类型, list ,array 等。
    ➢ open、close、separator 为对遍历内容的 SQL 拼接

    # 语法: 
    <foreach collection="集合类型" open="开始的字符" close="结束的字符"  
             item="集合中的成员" separator="集合成员之间的分隔符"> 
        #{item的值} 
    </foreach>
    
    • 遍历 List< 简单类型>

      表达式中的 List 使用 list 表示,其大小使用 list.size 表示。

      // 需求:查询学生 id 是 1002,1005,1006  
      // 接口方法: 
      List<Student> selectStudentForList(List<Integer> idList); 
      
      // mapper 文件: 
      <select id="selectStudentForList" 
      resultType="com.liu.domain.Student"> 
          select id,name,email,age from student 
          <if test="list !=null and list.size > 0 "> 
              where id in 
              <foreach collection="list" open="(" close=")"  item="stuid" separator=","> 
                  #{stuid} 
              </foreach> 
          </if> 
      </select> 
       
      // 测试方法: 
      @Test 
      public void testSelectForList()  { 
          List<Integer> list = new ArrayList<>(); 
          list.add(1002); 
          list.add(1005); 
          list.add(1006); 
       
          List<Student> studentList = studentDao.selectStudentForList(list); 
          studentList.forEach( stu -> System.out.println(stu)); 
      } 
      
    • 遍历 List< 对象类型>

      // 接口方法: 
      List<Student> selectStudentForList2(List<Student> stuList); 
       
      // mapper 文件: 
      <select id="selectStudentForList2" resultType="com.liu.domain.Student"> 
          select id,name,email,age from student 
          <if test="list !=null and list.size > 0 "> 
              where id in 
              <foreach collection="list" open="(" close=")" item="stuobject" separator=","> 
                  #{stuobject.id} 
              </foreach> 
          </if> 
      </select> 
      
       
      // 测试方法: 
      @Test 
      public void testSelectForList2()  { 
          List<Student> list = new ArrayList<>(); 
          Student s1 = new Student(); 
          s1.setId(1002); 
          list.add(s1); 
       
          s1 = new Student(); 
          s1.setId(1005); 
          list.add(s1); 
       
          List<Student> studentList = studentDao.selectStudentForList2(list); 
          studentList.forEach( stu -> System.out.println(stu)); 
      }
      
  • 动态 SQL 之代码片段

    标签用于定义 SQL 片断,以便其它 SQL 标签复用。而其它标签使用该 SQL 片断,需要使用子标签。 该标签可以定义 SQL 语句中的任何部分, 所以子标签可以放在动态 SQL的任何位置。

    // 接口方法: 
    List<Student> selectStudentSqlFragment(List<Student> stuList); 
     
    // mapper 文件: 
    <!-- 创建 sql  片段 id: 片段的自定义名称--> 
    <sql id="studentSql"> 
        select id,name,email,age from student 
    </sql> 
        
    <select id="selectStudentSqlFragment" resultType="com.liu.domain.Student"> 
        <!--  引用 sql  片段  --> 
        <include refid="studentSql"/>   
        
        <if test="list !=null and list.size > 0 "> 
            where id in 
            <foreach collection="list" open="(" close=")" item="stuobject" separator=","> 
                #{stuobject.id} 
            </foreach> 
        </if> 
    </select> 
    
     
    // 测试方法: 
    @Test 
    public void testSelectSqlFragment()  { 
        List<Student> list = new ArrayList<>(); 
        Student s1 = new Student(); 
        s1.setId(1002); 
        list.add(s1); 
     
        s1 = new Student(); 
        s1.setId(1005); 
        list.add(s1); 
     
        List<Student> studentList = studentDao.selectStudentSqlFragment(list); 
        studentList.forEach( stu -> System.out.println(stu)); 
    } 
    

MyBatis 配置文件

  • 主配置文件

    // 之前项目中使用的 mybatis.xml 是主配置文件。 
    // 主配置文件特点: 
    // 1. 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"> 
     
    // 2.根元素,<configuration> 
     
    // 3.主要包含内容: 
    // ➢  定义别名 
    // ➢  数据源 
    // ➢  mapper 文件
    
  • dataSource 标签

    ​ Mybatis 中访问数据库,可以连接池技术,但它采用的是自己的连接池技术。在 Mybatis 的 mybatis-config.xml配置文件中,通过来实现 Mybatis 中连接池的配置。

    Mybatis 将数据源分为三类:
    UNPOOLED 不使用连接池的数据源
    POOLED 使用连接池的数据源
    JNDI 使用 JNDI 实现的数据源

    ​ 其中 UNPOOLED ,POOLED 数据源实现了 javax.sq.DataSource 接口, JNDI 和前面两个实现方式不同, 了解可以。

  • dataSource 配置

    // 在 MyBatis.xml 主配置文件,配置 dataSource: 
    <dataSource type="POOLED"> 
        <!-- 连接数据库的四个要素--> 
        <property name="driver" value="com.mysql.jdbc.Driver"/> 
        <property name="url"                                                                               
    value="jdbc:mysql://localhost:3306/ssm?charset=utf-8"/> 
        <property name="username" value="root"/> 
        <property name="password" value=""/> 
    </dataSource> 
        
    // MyBatis 在初始化时,根据<dataSource>的 type 属性来创建相应类型的的数据源 DataSource,即: 
    // type=”POOLED” :MyBatis 会创建 PooledDataSource 实例  
    // type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例  
    // type=”JNDI” :MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
    
  • 事务

    • 默认需要手动提交事务

      Mybatis 框架是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC 的 Connection对象的 commit(), rollback() . Connection 对象的 setAutoCommit()方法来设置事务提交方式的。自动提交和手工提交、

      该标签用于指定MyBatis所使用的事务管理器。 MyBatis支持两种事务管理器类型: JDBC 与MANAGED。

      ➢ JDBC:使用 JDBC 的事务管理机制。即,通过 Connection 的 commit()方法提交,通过 rollback()方法回滚。但默认情况下,MyBatis 将自动提交功能关闭了,改为了手动提交。即程序中需要显式的对事务进行提交或回滚。

    • 自动提交事务

      设置自动提交的方式,factory 的 openSession() 分为有参数和无参数的。 有参数为 true,使用自动提交,可以修改 MyBatisUtil 的 getSqlSession()方法。session = factory.openSession(true); 再执行 insert 操作,无需执行session.commit(),事务是自动提交的。

  • 使用数据库属性配置文件

    为 了方便对数据库连接的管理, DB 连接四要素数据一般都是存放在一个专门的属性文件中的。 MyBatis主配置文件需要从这个属性文件中读取这些数据。

    • 在 classpath 路径下, 创建 properties 文件

    • 使用key指定值

      <dataSource type="POOLED"> 
          <!-- 使用 properties  文件:  语法 ${key}--> 
          <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>
      
    • typeAliases (类型别名)

      Mybatis 支持默认别名,我们也可以采用自定义别名方式来开发,主要使用在 mybatis.xml 主配置文件定义别名:

      <typeAliases> 
          <!-- 
             定义单个类型的别名 
            type: 类型的全限定名称 
            alias: 自定义别名  
              --> 
          <typeAlias type="com.liu.domain.Student" alias="mystudent"/> 
          <!-- 
             批量定义别名,扫描整个包下的类,别名为类名(首字母大写或小写都可以) 
            name: 包名  
              --> 
          <package name="com.bjpowernode.domain"/> 
          <package name="... 其他包"/> 
      </typeAliases> 
       
      # mapper.xml 文件,使用别名表示类型 
      <select id="selectStudents" resultType="mystudent"> 
             select id,name,email,age from student 
      </select> 
      
  • mappers (映射器)

    使用相对于类路径的资源,从 classpath 路径查找文件
    例如:

    指定包下的所有 Dao 接口 如: 注意:此种方法要求 Dao 接口名称和 mapper 映射文件名称相同,且在同一个目录中。
    ```
    • typeAliases (类型别名)

      Mybatis 支持默认别名,我们也可以采用自定义别名方式来开发,主要使用在 mybatis.xml 主配置文件定义别名:

      <typeAliases> 
          <!-- 
             定义单个类型的别名 
            type: 类型的全限定名称 
            alias: 自定义别名  
              --> 
          <typeAlias type="com.liu.domain.Student" alias="mystudent"/> 
          <!-- 
             批量定义别名,扫描整个包下的类,别名为类名(首字母大写或小写都可以) 
            name: 包名  
              --> 
          <package name="com.bjpowernode.domain"/> 
          <package name="... 其他包"/> 
      </typeAliases> 
       
      # mapper.xml 文件,使用别名表示类型 
      <select id="selectStudents" resultType="mystudent"> 
             select id,name,email,age from student 
      </select> 
      
  • mappers (映射器)

    使用相对于类路径的资源,从 classpath 路径查找文件
    例如:

    指定包下的所有 Dao 接口 如: 注意:此种方法要求 Dao 接口名称和 mapper 映射文件名称相同,且在同一个目录中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值