Mybatis笔记


Mybatis笔记

结构图


入门创建与简单CURD

SqlSession的创建

mybatis的maven依赖包

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

概念理解
每个基于MyBatis的应用都是以一个SqlSeesionFactory的实例为核心的,该实例可用SqlSessionFactoryBuilder获得,这个SSFB可以通过XML配置文件预先配置的Configuration实例构建。

使用XML构建SqlSessionFactory

方法流程

  1. 先完成xml配置文件的创建
  2. 获取xml文件路径,用资源获取输入流
  3. 用输入流获得SqlSessionFactory

xml配置文件
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>
  <environments default="development">
    <environment id="development">
      <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>

mapper中包含一组映射器,这些映射器包含了SQL代码和映射定义信息。

使用类路径下的资源文件进行配置并获取SqlSessionFactory
这三行还是比较固定的

// 文件路径
String resource = "org/mybatis/example/mybatis-config.xml";
// 利用资源获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 根据输入流获取SSF
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

不使用XML构建SqlSessionFactory

方法流程
使用java代码写的,具体没看懂,然而因为要用一些高级映射,所以还是会用到xml

获取代码

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

使用SqlSessionFactory获取SqlSession

SqlSession session = sqlSessionFactory.openSession()

作用域和生命周期和命名空间

作用域和生命周期

注意:使用依赖注入可以直接忽略生命周期(这块和Spring挂钩)。

生命周期最佳作用域
SqlSessionFactoryBuilder这个类可以实例化、使用、丢弃,一旦创建了SqlSessionFactory就不需要这个东西了最好是作为一个局部变量,方法作用域
SqlSessionFactory一旦被创建就一直运行,没有任何理由丢弃或重建另一个实例,也不应重复创建多次应用作用域
SqlSession每个线程都有自己的SqlSession实例,SqlSession实例不是线程安全的,不可共享请求或方法作用域,不可放在静态域中,不可放在托管域中,使用应立刻关闭,关闭应放到finally中
映射器实例在调用他们的方法中被获取,使用完毕后即可丢弃,不需要显示地关闭方法作用域

官方建议
方法作用域就是放在try里面那样子。

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

命名空间namespace

作用

  1. 用名字来将不同语句隔离,同时实现接口绑定,即使暂时不用,也要遵守
  2. 将命名空间放在何是的java包命名空间中,代码更整洁

两种命名规则

  1. 全限命名,如com.mypackage.MyMapper.selectAllThings
  2. 短命名,如selectAllThings。如果全局唯一可以如此,如果不唯一,则要用全名。

简单的CURD

方法流程

  1. 先创建一个接口类,该类中的方法相当于被Sql实现
  2. 用xml文件将mapper和接口绑定,xml文件中通过"id"来绑定对应的方法
  3. 调用sqlSession.getMapper(接口类.class)方法会获取的一个接口类的实例
  4. 使用该实例可调用接口的方法,来实现Sql的功能

准备工作

父工程maven配置文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!--这个叫父工程-->
    <groupId>com.it</groupId>
    <artifactId>Mybatis_learn</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>mybatis-01</module>
    </modules>

    <!--导入依赖-->
    <dependencies>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <!--Mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>

        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <!--我的是GBK,此处应为False-->
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

工具类

public class utils {
    public static SqlSessionFactory sqlSessionFactory;

    static {
        try{
            // 文件路径
            String resource = "config.xml";
            // 利用资源获得输入流, 这里有个异常
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 根据输入流获取SSF
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (Exception o){
            o.printStackTrace();
        }
    }

    public SqlSession geSS(){
        return sqlSessionFactory.openSession();
    }

}

数据库记录对应的实体类
该类和数据库记录相对应, 类中的私有成员变量名应和数据库中的相同。
当从数据库中取出数据的时候,就用该类接受。

// 实体类
public class User {
    // 名字和数据库里的一样
    private int id;
    private String name;
    private String pwd;

    public User() {
    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}

核心配置文件
管理事务、数据库连接、映射相关内容。
注意的点:

  1. 数据库url中useSSL是加密相关,设为false就没有验证了
  2. and功能要使用特殊符号:&amp
  3. mapper中用resource的话,路径必须是/
<?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">

        <environment id="development">
            <!--事务管理-->
            <transactionManager type="JDBC"/>
            <!--数据库相关-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--声明使用库-->
                <!--一直报错,我把数据校验,useSSL改成false就好了,目前不知道为啥-->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;userUnicode=true&amp;characterEncoding=gbk"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射器,路径以斜杠结尾-->
    <mappers>
        <mapper resource="it/dao/BlogMapper.xml"/>
    </mappers>
</configuration>

资源过滤
maven默认只能读取resources中的xml,使用下面的build,可以将检索范围扩大。
注意:

  1. filter是使用项目属性或系统属性代替文件属性。如果系统是utf-8,而文件是gbk,设置true的话可能乱码
  2. 该内容放在父工程中,会对所有的子工程生效
    <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>

做CURD需改动的内容

流程

  1. 编写接口类
  2. 修改对应mapper.xml中的内容进行绑定
  3. 测试,注意提交事务

接口类
需要实现的Sql功能的方法都在这里

// 得是个接口,参数和返回类型要和sql语句匹配
public interface BlogMapper {
    // 注释和xml不可共存
    // @Select("SELECT * FROM mybatis.user where id = #{id}")
    User getUser(int id);
    // insert, 参数为对应class
    int doInsert(User user);
    // update,参数为对应class
    int doUpdate(User user);
    // delete, 参数随意
    int doDelete(int id);

    // map用法,key为String类型,value为Object类型
    User getUserMap(Map<String, Object> map);

    // 模糊查询
    List<User> getUserMH(String string);
}

配置文件绑定
namespace和接口的绑定、接口类中的方法和xml中的SQL语句的绑定。
注意:

  1. namespace中的包名要和接口一样
  2. id对应namespace绑定的接口类的方法名,要一致
  3. resultType是返回值的类型,如果返回的是List<E>,那么这里也要是E
  4. paramterType是参数的类型,只有一个基本数据类型的情况,可以直接在SQL中获取,可不写;如果是多参数的话,用map或者注解好一些
<?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接口-->
<!--相当于实现了接口-->
<mapper namespace="it.dao.BlogMapper">
    <!--id相当于实现方法的函数名,不能错-->
    <!--返回要写权限命名,即使是集合,也写泛型里的东西-->
    <select id="getUser" resultType="it.User">
        select * from mybatis.user where id = #{id}
    </select>
    <!--该操作没有resultType类型-->
    <insert id="doInsert" parameterType="it.User">
        insert into mybatis.user(id, Name, pwd) value (#{id}, #{name}, #{pwd});
    </insert>

    <update id="doUpdate" parameterType="it.User">
        update mybatis.user set Name=#{name}, pwd=#{pwd} where id=#{id};
    </update>

    <delete id="doDelete" parameterType="int">
        delete from mybatis.user where id=#{id};
    </delete>

    <!--使用map-->
    <select id="getUserMap" parameterType="map" resultType="it.User">
        select * from mybatis.user where id=#{iddd};
    </select>

    <!--模糊查询-->
    <!--注意即使是返回的集合,返回类型也是类-->
    <select id="getUserMH" resultType="it.User">
        select * from mybatis.user where name like #{value};
    </select>
</mapper>

测试类

public class dot {
    public static void main(String[] args) {
        utils g = new utils();
        SqlSession sqlSession = g.geSS();
        // 方法一
        // 获取映射器
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        // 调用映射器方法
        User user = mapper.getUser(1);
        System.out.println(user.toString());
        int jojo = mapper.doUpdate(new User(1, "jojo", "1234"));
        System.out.println(jojo);
        mapper.doDelete(2);
        mapper.doInsert(new User(5, "ll","123"));
        sqlSession.commit();
        sqlSession.close();
        // 方法二
        // User user1 = sqlSession.selectOne("it.dao.BlogMapper.getUser", 2);
        // System.out.println(user1.toString());
        // sqlSession.close();

    }

    @Test
    public void testMap(){
        utils g = new utils();
        SqlSession sqlSession = g.geSS();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        // 此处要定义一个map
        Map<String, Object> map =new HashMap<String, Object>();
        map.put("iddd",1);
        User userMap = mapper.getUserMap(map);
        System.out.println(userMap.toString());

        // 模糊查询
        System.out.println("-------------");
        List<User> list = mapper.getUserMH("%李%");
        for( User use: list){
            System.out.println(use.toString());
        }
    }
}

Map和模糊查询

这两个在上面的代码中有体现

Map

  • 当实体类、或者数据库中的表、字段较多,建议使用map;当user类不能为空的时候,如果只想改变一个,那么就很不方便,使用map可以只改变一个。
  • String是key,Object是value
  • Map传递参数直接在SQL取出key即可, parameterType为map,所以要制作一个map作为参数。

模糊查询

  • 测试中使用通配符作为传递参数,如`"%李%""

配置

环境配置

多个环境的必要性:尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境,即每个数据库对应一个SqlSessionFactory实例。

环境配置文件

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <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>

生成时指定环境
这里的environment使用的是id属性

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

生成时不指定环境
使用default指定的环境

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);

配置读取

config.properties文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&userUnicode=true&characterEncoding=gbk

涉及到properties部分的配置代码


    <!--properties必须放在第一个-->
    <properties resource="config.properties">
        <!--这个叫properties元素体内的属性-->
        <!--这里也可以定义元素属性,作为resource的补充-->
        <!--当这里和resource发生冲突时,以resource中为主-->
        <property name="username" value="root"/>
        <!--启用默认特性-->
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
    </properties>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <!--如果password没有被配置,则使用:后面的值-->
                <property name="password" value="${password:1234}"/>
            </dataSource>
        </environment>
    </environments>

properties读取优先级

  • 最高级:方法参数传递的
  • 中级:resource读取的属性,即config.properties文件
  • 最低级:properties元素内指定属性,即xml中自己设置的

注意点

  • properties不需要转义,所以url要改动。
  • properties必须放在xml文件中的最上面。

别名

三种起别名方式

  1. 为单个实体类单独指定
  2. 为整个包中的实体类起别名,默认使用类的首字母小写形式(不过测试了大写也可以)
  3. 在实体类上边加注释

xml中代码

    <!--别名,只能对实体类取别名-->
    <typeAliases>
        <typeAlias alias="user" type="it.User"/>
        <!--对包中所有实体类起别名,使用首字母小写的形式-->
        <package name="it"/>
    </typeAliases>

实体类中代码

// 实体类
@Alias("user")
public class User {}

优先级:typeAlias > 注解 > package


映射器配置

三种方式

  1. 通过resource配置(推荐该方法)
  2. 通过类配置
  3. 通过包配置

注意

  • 类和包的方法都有两个限制条件:1. 配置文件和接口同名;2. 两个文件在同一目录下。

xml文件代码

<mappers>
    <!--通过resource的方式, 推荐该方式-->
    <mapper resource="it/dao/BlogMapper.xml"/>

    <!--通过类的方式-->
    <!--该方法有两个限制条件:1. 配置文件和接口同名;2. 两个文件在同一目录下-->
    <mapper class="it.dao.BlogMapper"/>

    <!--通过包的方式-->
    <!--也需要同名.同文件夹下-->
    <package name="it.dao"/>
</mappers>


解决属性名和字段名不一致的问题

问题描述:接受数据库数据的类中的成员变量名和数据库的字段不一样

出现问题原因
一般情况下,MyBatis会自动创建一个ResultMap,在基于属性名映射column到实体类的属性上,没匹配就会出现null。

最简单最笨的解决方式
就是sql的起别名:

select id, name, pwd as passowrd from user where id = #{id}

使用方法
把ResultType换成ResultMap
两种名称:

id   name   pwd
id   name   password

结果集映射:

<!--结果集映射-->
<resultMap id="UserMap" type="User">
    <!--column数据库中的字段,property实体类中的属性-->
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>

<select id="getUserById" resultMap="UserMap">
    select * from mybatis.user where id = #{id}
</select>
  • resultMap元素是 MyBatis 中最重要最强大的元素
  • ResultMap的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。

日志工厂

日志的作用:如果一个数据库操作,出现了异常,需要排错,就要用日志。

日志类型

  • SLF4J
  • LOG4J 【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 【掌握】
  • NO_LOGGING

Log4j使用步骤

1. 导入Log4j的maven

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2. log4j.properties
属性设置文件,放到resource中,名字乱写会报错

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3. mybatis配置文件中编写settings

    <settings>
        <!--标准日志,配置都设置好了,可以直接用-->
        <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>

4. 实例化日志对象
参数为当前类的class

    static Logger logger = Logger.getLogger(dot.class);

5. 日志的级别:

public  void testlog4j(){
    logger.info("info");
    logger.debug("debug");
    logger.error("error");
}

配对问题
好像是只要在配置文件中声明了setting,然后在这个项目里不管调用什么函数都会按照日志配置文件操作。
至于Logger加载的class有啥用,现在还不知道,可能是监控这个类的所有行为?


分页

sql分页基础知识:

// 从startIndex起,每页放pageSize个
select * from user limit startIndex, pageSize;
// 从第4个起,一直到最后,是个bug,被修复了
// select * from user limit 4, -1;

// 只有一个参数,就是从0开始到n
select * from user limit n;

使用Limit分页

步骤

  1. 创建一个方法接口和SQL语句
    List<User> getPage(Map<String, Integer> map);
    <select id="getPage" parameterType="map" resultMap="userMap">
        select * from mybatis.user limit #{startIndex}, #{pageSize}
    </select>
  1. 调用方法
    public void testLimi(){
        utils g = new utils();
        SqlSession sqlSession = g.geSS();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        // 设置key和value
        HashMap<String, Integer> map = new HashMap<String, Integer>();
        map.put("startIndex", 1);
        map.put("pageSize", 2);
        List<User> page = mapper.getPage(map);

        for (User user : page){
            System.out.println(user.toString());
        }

        sqlSession.close();
    }

RowBounds分页

这个方法不需要sql

步骤

  1. 接口
List<User> getUserByRowBounds();
  1. mapper.xml
<select id="getUserByRowBounds" resultMap="UserMap">
    select * from  mybatis.user
</select>
  1. 方法
@Test
public void getUserByRowBounds(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    //RowBounds实现
    RowBounds rowBounds = new RowBounds(1, 2);

    //通过Java代码层面实现分页
    List<User> userList =  sqlSession.selectList("com.kuang.dao.UserMapper.getUserByRowBounds",null,rowBounds);

    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}

使用注解开发

面向接口编程

根本原因:解耦,可拓展,提高复用,分成开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好。

关于接口的理解

  • 定义与实现的分离
  • 对于系统的抽象理解
  • 有两类
    • 对一个个体的抽象,可对应为一个抽象体(abstract class)
    • 对一个个体某一方面的抽象,即形成一个抽象面(interface)
  • 一个个体可能有多个抽象面,抽象体和抽象面是有区别的

本质:主要应用反射
底层:动态代理

使用方法:

  1. 定义接口类的方法
  2. 在核心配置文件中绑定接口,而不是xml,也没有xml
  3. 在接口的方法上写上注解,注意参数命名

接口

public interface BlogMapper {
    @Select("select * from mybatis.user where id=#{uid}")
    public User getUser(@Param("uid") int id);

    // 做不到参数map
    @Insert("Insert into mybatis.user(id, name, pwd) values(#{id},#{name},#{password})")
    public int doInsert(User user);

    @Update("update user set name=#{name}, pwd=#{password} where id=#{id}")
    public int doUpdate(User user);

    @Delete("delete from user where id=#{id}")
    public int doDelete(int id);
}

核心配置文件变化
绑定一个接口类

    <mappers>
        <mapper class="it.dao.BlogMapper"/>
    </mappers>

测试类

@Test
public void testJunit(){
    utils g = new utils();
    SqlSession sqlSession = g.geSS();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    System.out.println("查询:");
    User user1 = mapper.getUser(1);
    System.out.println(user1.toString());
    mapper.doInsert(new User(7, "hhh", "111"));
    mapper.doDelete(5);
    mapper.doUpdate(new User(4,"hxh","22"));
}

注意

  • 可以在工具类创建的时候实现自动提交事务,ssf.openSession(true)设置为true可自动提交。
  • 方法存在多个参数时,基本类型都加@Param("id"),sql中的参数名要和Param的一样。
  • 这个注解指定名字也可以在mapper.xml中使用,相当于给参数起了别名。

Lombok

使用步骤

  1. 添加插件
  2. 导入lombok的jar包
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
    <scope>provided</scope>
</dependency>
  1. 通过加注解来自动完成一些事情。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    // 名字和数据库里的一样
    private int id;
    private String name;
    private String password;
}

复杂查询环境搭建

多对一

按照查询嵌套

流程

  1. 先整体选择,把返回值类型改一下,改为resultMap并自定义map。
  2. 定义map,type设置为原本应该接受的类型,后面的变动的地方用associationcollection来改变,javaType是想要的类型,再用一个select从另一个sql中获取信息。
  3. mybatis会自动估计另一个select的参数,resultType为想要的类型。

思路
全选出来,先选出来的结果要被接收,然后在第一次输出的结果中再去查找。

代码

     // 查询所有的学生信息和对应的老师的信息
    public List<Student> getStudent();
    <!--这边就直接选择了,靠结果map来改变-->
    <select id="getStudent" resultMap="StudentTeacher">
        select * from mybatis.student
    </select>
    <!--嵌套查询-->
    <resultMap id="StudentTeacher" type="Student">
        <!--简单的属性用result且不用变-->
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--复杂的要用关联,property还是原本的类的名字, column还是数据库中的字段-->
        <!--对象是association-->
        <!--集合时collection-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>


    <select id="getTeacher" resultType="Teacher">
        select * from mybatis.teacher where id=#{tid}
    </select>

结果嵌套处理

流程

  1. 也是要做个resultMap,不过sql语句可以写成想要的那种
  2. 通过对resultMap的修改,来让结果拟合之前的sql语句

思路

  • sql语句返回的就是想要的,但是类型并不是已有的,所以要用map来做个新的类型
  • map做的新类型中的变量应大于等于sql获取的,可多不可少
  • association就是把对应的属性赋值进去了。

代码

public List<Student> getStudent2();
    <!--按照结果嵌套处理-->
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid, s.name sname, t.name tname
        from mybatis.student s, mybatis.teacher t
        where s.tid = t.id
    </select>

    <resultMap id="StudentTeacher2" type="Student">
        <!--现在得到的column时上面的sql获取来的,名字也按上面来-->
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>

一对多处理

两种方法,如果结果是集合,resultMap中应用collection
第一种方式:

    <select id="getStudentsByTeacher2" resultMap="StuTeacher">
        select * from mybatis.teacher where id=#{tid}
    </select>

    <resultMap id="StuTeacher" type="Teacher">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--这边的column不应用tid-->
        <!--List类型很不一样-->
        <!--这里column做id-->
        <collection property="students" javaType="ArrayList" ofType="Student" select="getStudents" column="id"/>
    </resultMap>


    <!--这里稍微有点问题-->
    <select id="getStudents" resultType="Student">
        select * from mybatis.student where tid=#{tid}
    </select>

第二种方式:

    <select id="getStudentsByTeacher" resultMap="Students">
        select s.name sname, s.id sid, t.name tname, t.id tid
        from mybatis.student s, mybatis.teacher t
        where s.tid=t.id and t.id=#{tid};
    </select>

    <!--type为原本类型-->
    <resultMap id="Students" type="Teacher">
        <result property="name" column="tname"/>
        <result property="id" column="tid"/>
        <!--配置students类中的属性-->
        <!--javaType指定属性的类型-->
        <!--但是如果是集合,要用ofType获取,还要用collection才行-->
        <collection property="students" ofType="Student">
            <result property="name" column="sname"/>
            <result property="id" column="sid"/>
        </collection>
    </resultMap>

小结

  1. 关联 - association 【多对一】
  2. 集合 - collection 【一对多】
  3. javaType & ofType
    1. JavaType 用来指定实体类中属性的类型
    2. ofType 用来指定映射到List或者集合中的 pojo类型,泛型中的约束类型!

动态SQL

比较好理解,有几个关键方法:

  • where:包含方法,像是开启判断
  • if:可用map来传递参数
  • when与choose:就像带了break的case,满足第一个就自动停了,where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
  • set:update的时候用
  • trim:自定义用的,好像是处理andor

代码总结

接口类

public interface BlogMapper {
    // 不要乱设置参数名
    int InsertBlog(Blog blog);

    // if查找
    List<Blog> getByIf(Map<String,Object> map);

    // 测试where
    List<Blog> getUseWhere(Map<String,Object> map);

    // 测试choose
    List<Blog> getUseChoose(Map<String,Object> map);

    // 测试set
    int doUpdate(Map<String, Object> map);
}
<mapper namespace="it.dao.BlogMapper">
    <insert id="InsertBlog" parameterType="Blog">
        insert into mybatis.blog(id, title, author, create_time, views)
        values (#{id}, #{title}, #{author}, #{createTime}, #{views})
    </insert>

    <!--要加个没用的条件1=1,不然语法就错了-->
    <select id="getByIf" parameterType="map" resultType="Blog">
        select * from mybatis.blog where 1=1
        <if test="author !=null ">
            and author=#{author}
        </if>
        <if test="views !=null ">
            and views=#{views}
        </if>
    </select>

    <!--有了where就不需要考虑and,会根据情况自动取舍and-->
    <select id="getUseWhere" parameterType="map" resultType="Blog">
        select * from mybatis.blog
        <where>
            <if test="author !=null ">
                and author=#{author}
            </if>
            <if test="views !=null ">
                and views=#{views}
            </if>
        </where>
    </select>

    <!--choose就像switch, when相当于带了break的case,遇到了就结束-->
    <!--otherwise里面不应该有参数了-->
    <select id="getUseChoose" parameterType="map" resultType="Blog">
        select * from mybatis.blog
        <where>
            <choose>
                <when test="title != null">
                    title=#{title}
                </when>
                <when test="author != null">
                    and author=#{author}
                </when>
                <otherwise>
                    views=2
                </otherwise>
            </choose>
        </where>
    </select>

    <!--之所以没写set,是因为set能动态前置,也能删除无关的逗号-->
    <update id="doUpdate" parameterType="map">
        update mybatis.blog
          <set>
              <if test="title != null">
                  title=#{title},
              </if>
              <if test="views != null">
                  views=#{views}
              </if>
          </set>
        where author=#{author}
    </update>
</mapper>

测试类

    @Test
    public void testIf(){
        utils uu = new utils();
        SqlSession sqlSession = uu.geSS();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Map<String, Object> map = new HashMap<String, Object>();
        List<Blog> byIf = mapper.getByIf(map);
        System.out.println(byIf);
        System.out.println("使用了条件");
        map.put("author","jojo");
        List<Blog> byIf2 = mapper.getByIf(map);
        System.out.println(byIf2);

    }

    @Test
    public void testWhere(){
        utils uu = new utils();
        SqlSession sqlSession = uu.geSS();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Map<String, Object> map = new HashMap<String, Object>();
        List<Blog> useWhereNo = mapper.getUseWhere(map);
        System.out.println(useWhereNo);
        System.out.println("使用map=====================");
        map.put("author","jojo");
        List<Blog> useWhere = mapper.getUseWhere(map);
        System.out.println(useWhere);
    }

    @Test
    public void testChoose(){
        utils uu = new utils();
        SqlSession sqlSession = uu.geSS();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Map<String, Object> map = new HashMap<String, Object>();
        System.out.println("空的,views=2的数据:");
        List<Blog> choose1 = mapper.getUseChoose(map);
        System.out.println(choose1);
        System.out.println("title=java, author=kiruto的情况=================");
        map.put("title", "java");
        map.put("author", "kiruto");
        List<Blog> choose2 = mapper.getUseChoose(map);
        System.out.println(choose2);
    }

    @Test
    public void testSet(){
        utils uu = new utils();
        SqlSession sqlSession = uu.geSS();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("author", "jojo");
        map.put("title", "change");
        mapper.doUpdate(map);
        sqlSession.commit();
        sqlSession.close();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值