Mybatis学习笔记

mybatis官网:https://mybatis.org/mybatis-3/zh/getting-started.html
在这里插入图片描述

搭建环境

导入依赖,以及修改过滤规则

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.18</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>

	<dependency>
	    <groupId>org.projectlombok</groupId>
	    <artifactId>lombok</artifactId>
	    <version>1.18.12</version>
	    <scope>provided</scope>
	</dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>
</dependencies>

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

创建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>
    <!--  设置实体类的别名  -->
    <typeAliases>
		
    </typeAliases>

    <!--  数据库连接  -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/angenin?useSSL=false&amp;serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!--  注册mapper文件  -->
    <mappers>

    </mappers>
</configuration>

地址报红解决办法:
在这里插入图片描述
在这里插入图片描述

编写工具类

public class MybatisUtil {

    private static SqlSessionFactory sqlSessionFactory;
    private static String resource = "mybatis-config.xml";

    static {
        try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
            // 创建SqlSessionFactory工厂对象
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 获取SqlSession对象
    public static SqlSession getSqlSession() {
	    // 往openSession()中传入true后,生成的SqlSession会自动提交事务
        return sqlSessionFactory.openSession();
    }

}

创建数据表

create table t_users(
	`id` int primary key auto_increment,
	`name` varchar(15) not null,
	`password` varchar(20) not null
);

insert into t_users(name, password)values("111", "111");
insert into t_users(name, password)values("222", "222");

创建user实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private int id;
    private String name;
    private String password;
}

创建UserMapper接口

public interface UserMapper {
    List<User> getUserList();
}

创建UserMapper.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">
<mapper namespace="com.angenin.dao.UserMapper">
    <select id="getUserList" resultType="com.angenin.pojo.User">
        select * from t_users;
    </select>
</mapper>

在mybatis-config.xml里注册UserMapper

<!--  注册mapper文件  -->
<mappers>
	<!-- 目录按斜杠/分隔 -->
    <mapper resource="com/angenin/dao/UserMapper.xml"/>
</mappers>

测试

@Test
public void test01() {
    // 获取sqlSession对象
    SqlSession sqlSession = MybatisUtil.getSqlSession();

    // 获取UserMapper对象
    UserMapper userDAO = sqlSession.getMapper(UserMapper.class);
    for (User user : userDAO.getUserList()) {
        System.out.println(user);
    }

    // 关闭sqlSession对象
    sqlSession.close();
}

目录结构:
在这里插入图片描述
如果mapper.xml文件想放到resources目录下,创建的目录路径必须为com/angenin/dao(注意:resources目录下是按照斜杆/进行目录分隔的),对应类的路径,把mapper文件放到这个目录下,如果只是用com.angenin.dao命名,其实只是一层目录,在target包下也可以看出生成的不是文件不是放在一起。

关于SqlSessionFactoryBuilder、SqlSessionFactory和SqlSession的注意事项:
在这里插入图片描述

mapper文件的命名空间namespace对应DAO接口的全限定类名,也是通过dao和mapper相关联,select标签的id对应DAO接口中方法的方法名,通过id相关联。

CRUD

UserMapper

public interface UserMapper {
	// 获取所有数据
    List<User> getUserList();
	// 获取单个数据
    User getOneUser(int id);
	// 新增一个用户(不需要设置id,数据库自增)
    void saveUser(User user);
	// 修改一个用户(通过id)
    void changeUser(User user);
	// 删除一个用户
    void removeUser(int id);
}

UserMapper.xml


   <select id="getOneUser" parameterType="int" resultType="com.angenin.pojo.User">
       select * from t_users where id=#{id};
   </select>

    <insert id="saveUser" parameterType="com.angenin.pojo.User">
        insert into t_users(name,password)values(#{name},#{password});
    </insert>

    <update id="changeUser" parameterType="com.angenin.pojo.User">
        update t_users set name=#{name},password=#{password} where id=#{id};
    </update>

    <delete id="removeUser" parameterType="int">
        delete from t_users where id=#{id};
    </delete>

测试:

	// 测试查询单个用户
    @Test
    public void test02() {
        // 获取sqlSession对象
        SqlSession sqlSession = MybatisUtil.getSqlSession();

        // 获取UserDAO对象
        UserMapper userDAO = sqlSession.getMapper(UserMapper.class);
        System.out.println(userDAO.getOneUser(1));

        // 关闭sqlSession对象
        sqlSession.close();
    }

	// 测试新增单个用户
    @Test
    public void test03() {
        // 获取sqlSession对象
        SqlSession sqlSession = MybatisUtil.getSqlSession();

        // 获取UserDAO对象
        UserMapper userDAO = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setName("test");
        user.setPassword("test");
        userDAO.saveUser(user);

        // 提交事务
        sqlSession.commit();

        // 关闭sqlSession对象
        sqlSession.close();
    }

	// 测试修改单个用户
    @Test
    public void test04() {
        // 获取sqlSession对象
        SqlSession sqlSession = MybatisUtil.getSqlSession();

        // 获取UserDAO对象
        UserMapper userDAO = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setId(38);
        user.setName("test2");
        user.setPassword("test2");
        userDAO.changeUser(user);

        // 提交事务
        sqlSession.commit();

        // 关闭sqlSession对象
        sqlSession.close();
    }

	// 测试删除单个用户
    @Test
    public void test05() {
        // 获取sqlSession对象
        SqlSession sqlSession = MybatisUtil.getSqlSession();

        // 获取UserDAO对象
        UserMapper userDAO = sqlSession.getMapper(UserMapper.class);
        userDAO.removeUser(38);

        // 提交事务
        sqlSession.commit();

        // 关闭sqlSession对象
        sqlSession.close();
    }

注意:因为mybatis默认关闭自动提交,所以执行完后需要手动提交事务,如果程序执行没有报错,但是数据库中没变化,可能就是这个原因。

#{} 与 ${} 的区别

#{}:会预编译,把sql语句中#{}的位置用?代替,在执行JDBC时才进行赋值,防止sql注入。
${}:字符串拼接,直接把对应的值拼接到sql语句中,存在sql注入的问题,但在使用order by排序时,使用的是${}。

Map

当我们只需要修改一条记录中的某些字段时,使用map可以指定修改指定字段,不需要新建对象。

UserMapper

    void changeUser2(Map<String, Object> map);

UserMapper.xml

    <update id="changeUser2" parameterType="map">
        update t_users set name=#{username},password=#{userpwd} where id=#{userid};
    </update>

测试

    @Test
    public void test06() {
        // 获取sqlSession对象
        SqlSession sqlSession = MybatisUtil.getSqlSession();

        // 获取UserDAO对象
        UserMapper userDAO = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> map = new HashMap<>();
        // 通过id修改密码
        map.put("userid", 40);
        map.put("userpwd", "pwd");
        userDAO.changeUser2(map);

        // 提交事务
        sqlSession.commit();

        // 关闭sqlSession对象
        sqlSession.close();
    }

mapper的#{}字段名通过map中的key找到value,如果类型是类,则是类属性名。

模糊查询

    List<User> getUserLike();
    <select id="getUserLike" parameterType="string" resultType="com.angenin.pojo.User">
        select * from t_users where name like #{value};
    </select>
    @Test
    public void test07() {
        // 获取sqlSession对象
        SqlSession sqlSession = MybatisUtil.getSqlSession();

        // 获取UserDAO对象
        UserMapper userDAO = sqlSession.getMapper(UserMapper.class);
        List<User> list = userDAO.getUserLike("%test%");
        for (User user : list) {
            System.out.println(user);
        }

        // 关闭sqlSession对象
        sqlSession.close();
    }

核心配置

在这里插入图片描述

引入配置文件(properties)

创建db.properties文件,把数据库的配置信息抽取出来

db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/angenin?useSSL=false&serverTimezone=Asia/Shanghai
db.username=root
db.password=123456
    <!--引入外部配置文件-->
    <properties resource="db.properties">
    	<!--可以在这里设置配置信息,如果内部和外部文件都设置了同一个属性名,以外部文件为准,如果这里不需要设置配置信息,使用单标签即可-->
        <!--<property name="db.username" value="root"/>-->
        <!--<property name="db.password" value="11111"/>-->
    </properties>

    <!--  数据库连接  -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>

注意:配置文件中的标签需要按照一定的顺序进行书写。

properties -> settings -> typeAliases -> typeHandlers -> objectFactory -> objectWrapperFactory -> reflectorFactory -> plugins -> environments -> databaseIdProvider -> mappers
在这里插入图片描述

设置(settings)

文档:https://mybatis.org/mybatis-3/zh/configuration.html#settings

一个配置完整的 settings 元素的示例如下:
mybatis-config.xml

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

settings有很多设置,最关键是下面三个:

  • cacheEnabled:缓存,true为开启。
  • lazyLoadingEnabled:懒加载,true为开启。
  • logImpl:日志,可设置SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING。

别名(typeAliases)

在mapper文件中,要求我们使用全限定类名,这样会让我们的代码看起来很长,我们可以在mybatis的配置文件中设置类的别名,在mapper就可以不需要写全限定类名了。
在这里插入图片描述
在这里插入图片描述

<!--设置实体类的别名  -->
<typeAliases>
    <typeAlias type="com.angenin.pojo.User" alias="User"/>
</typeAliases>

typeAliases属性下还有另一个标签package,是通过扫描指定包下的所有类自动设置别名,一般我们使用typeAlias标签,别名设置为首字母设置为大写,而包扫描默认为首字母小写,这样可以便于我们区分是用哪种方式设置的别名。

<typeAliases>
    <package name="com.angenin.pojo"/>
</typeAliases>

我们可以在扫描包下的实体类上加上@Alias来自定义别名。

@Alias("u")
public class User implements Serializable {}

在这里插入图片描述
我们只要记住加了下划线的为基本类型,没加的为其包装类即可。

数据库环境(environments)

environments中可配置多个环境,通过default属性指定使用哪个环境。

    <!--  default为test,所以是使用id为test的环境  -->
    <environments default="test">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                ...
            </dataSource>
        </environment>
        
        <environment id="test">
            <transactionManager type=""></transactionManager>
            <dataSource type=""></dataSource>
        </environment>
    </environments>

事务管理器(transactionManager)

有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC:使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED:这个配置几乎没做什么,了解即可。

数据源(dataSource)
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):

  • UNPOOLED:这个数据源的实现会每次请求时打开和关闭连接,即没有池。
  • POOLED:利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。
  • JNDI:为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

具体配置参数可以自行查看文档:
https://mybatis.org/mybatis-3/zh/configuration.html#environments

映射器(mappers)

mappers有mapper和package两种方式,而mapper有三种方式。
在这里插入图片描述
不推荐使用url的方式。

除了resource,其他方式都需要接口和mapper文件在同个包下,并且需要同名

生命周期和作用域

不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。

  • SqlSessionFactoryBuilder
    可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它。

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

  • SqlSession
    每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域,每个请求对应一个新的SqlSession,并且使用完后立即销毁。

解决数据库和Java实体类属性名不一致的问题

我们把User类的属性名修改为password改为pwd,这样我们实体类属性名和数据库对应的表的字段名就都不一致了,不一致会导致赋值不成功,对应的属性为null。

解决办法:

  1. SQL语句使用as

    <select id="getUserList" resultType="User">
        select password as pwd from t_users;
    </select>
    
  2. 封装为结果集映射(ResultMap)
    只需要封装不一致的属性即可。

    <resultMap id="UserMap" type="com.angenin.pojo.User">
        <!-- column对应数据库表的字段名,property对应java实体类的属性名 -->
        <result property="pwd" column="password"/>
    </resultMap>
    
    
    <select id="getUserList" resultMap="UserMap">
        select * from t_users;
    </select>
    

关于ResultMap更具体的用法会在下面写出。

日志

settings的 logImpl,可设置:

  • SLF4J
  • LOG4J【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING【掌握】
  • NO_LOGGING
  1. 使用STDOUT_LOGGING
    内置,可直接使用。

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    
  2. 使用LOG4J

    1. 需要导入log4j的jar包。
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
    1. 创建log4j的配置文件log4j.properties
    # 配置根
    log4j.rootLogger=debug,console
    # 日志输出到控制台显示
    log4j.appender.console=org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target=System.out
    log4j.appender.console.layout=org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n	
    

    需要更详细的配置自行百度。

    1. 将日志设置log4j
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    

limit分页

减少数据的处理量。

UserMapper

   // 分页
   List<User> getUserLimit(Map<String, Integer> map);

UserMapper.xml

   <select id="getUserLimit" parameterType="map" resultMap="UserMap">
       select * from t_users limit #{startIndex},#{pageSize};
   </select>

测试

   @Test
   public void test08() {
       // 获取sqlSession对象
       SqlSession sqlSession = MybatisUtil.getSqlSession();

       // 获取UserDAO对象
       UserMapper userDAO = sqlSession.getMapper(UserMapper.class);
       Map<String, Integer> map = new HashMap<>();
       map.put("startIndex", 2);
       map.put("pageSize", 2);
       List<User> list = userDAO.getUserLimit(map);
       for (User user : list) {
           System.out.println(user);
       }

       // 关闭sqlSession对象
       sqlSession.close();
   }

关于limit第二个参数为-1的问题:为第二个参数为-1时,会从第一个参数开始一直查找到最后,mysql认为它是bug,现在已经修复了,所以现在mysql高版本中limit的第二个参数不能小于0。

分页插件:PageHelper
分页插件底层也是使用limit,有兴趣的可以自行参照文档学习使用。

注解开发

把UserMapper.xml移出dao包,然后在UserMapper接口的getUserList方法上加上@Select注解,在注解里写上sql语句,增删改用法类似。

UserMapper

public interface UserMapper {
	// 查询所有
    @Select("select * from t_users")
    List<User> getUserList();
	
	// 查询单个
    @Select("select * from t_users where id=#{id}")
    User getOneUser(@Param("id") int id);

	// 新增
    @Insert("insert into t_users(name,password)values(#{name},#{pwd})")
    void saveUser(User user);

	// 修改
    @Update("update t_users set name=#{name}, password=#{pwd} where id=#{id}")
    void changeUser(User user);
    
    // 删除
    @Delete("delete from t_users where id=#{id}")
    void removeUser(@Param("id") int id);
}

关于@Param:如果有@Param,#{}中属性名对应@Param指定的属性名,基本类型或者String类型的参数,需要加上,指定属性名,引用类型不需要加,只有一个基本类型或String的参数时也可以不加。

绑定接口

    <mappers>
        <!-- 目录按斜杠/分隔 -->
<!--        <mapper resource="com/angenin/dao/*Mapper.xml"/>-->
        <mapper class="com.angenin.dao.UserMapper"/>
    </mappers>

使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

底层:动态代理 + 反射

resultMap处理多对一

创建教师表和学生表(学生表通过外键关联教师表)

create table teacher(
	`id` int primary key auto_increment,
	`name` varchar(15) default null
);

insert into teacher(`name`)values("李老师");

create table student(
	`id` int primary key auto_increment,
	`name` varchar(15) default null,
	`tid` int,
	foreign key(tid) references teacher(id)
);

insert into student(`name`,`tid`)values("小明",1);
insert into student(`name`,`tid`)values("小红",1);
insert into student(`name`,`tid`)values("小刚",1);

创建学生和教师实体类

@Data
public class Student {
    private int id;
    private String name;
    private Teacher teacher;
}
@Data
public class Teacher {
    private int id;
    private String name;
    private List<Student>  students;
}

按照查询嵌套处理

StudentMapper

public interface StudentMapper {
	// 获取学生表中的所有学生,并显示其对应的教师名
    List<Student> getStudents();
}

StudentMapper.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">
<mapper namespace="com.angenin.dao.StudentMapper">
    
    <resultMap id="StudentTeacher" type="Student">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!--复杂属性需要单独处理,association:对象,collection:集合-->
        <association property="teacher" javaType="Teacher" column="tid" select="getTeacher"/>
    </resultMap>
    
    <select id="getStudents" resultMap="StudentTeacher">
        select * from student;
    </select>
    <!--报红没关系-->
    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id=#{tid};
    </select>
</mapper>

mybatis-config.xml

<typeAliases>
    <package name="com.angenin.pojo"/>
</typeAliases>

<mappers>
    <mapper resource="com/angenin/dao/StudentMapper.xml"/>
</mappers>

测试

@Test
public void test09() {
    SqlSession sqlSession = MybatisUtil.getSqlSession();

    StudentMapper studentDAO = sqlSession.getMapper(StudentMapper.class);
    List<Student> students = studentDAO.getStudents();
    for (Student student : students) {
        System.out.println(student);
    }

    sqlSession.close();
}

按照结果嵌套处理

StudentMapper

List<Student> getStudents2();

StudentMapper.xml

    <resultMap id="StudentTeacher2" type="Student">
        <!--数据表字段名对应as别名-->
        <id property="id" column="sid"/>
        <result property="name" column="sname"/>
        <!--第一种写法-->
        <!--复杂属性需要单独处理,association:对象,collection:集合-->
<!--        <association property="teacher" javaType="Teacher">-->
<!--            <result property="name" column="tname"/>-->
<!--        </association>-->
        <!--第二种写法-->
        <result property="teacher.name" column="tname"/>
    </resultMap>

    <select id="getStudents2" resultMap="StudentTeacher2">
        select s.id as sid,s.name as sname,t.name as tname
        from student as s, teacher as t
        where s.tid=t.id;
    </select>

官方也推荐用按照结果嵌套处理的方式进行处理。

resultMap处理一对多

TeacherMapper

public interface TeacherMapper {
	// 通过id获取指定教师,及其对应的所有学生
    Teacher getTeacher(@Param("tid")int id);
}

TeacherMapper.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">
<mapper namespace="com.angenin.dao.TeacherMapper">

    <!--按照结果嵌套查询-->
    <resultMap id="TeacherStudent" type="Teacher">
        <id property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--复杂属性需要单独处理,association:对象,collection:集合-->
        <!--ofType:指定list泛型的属性,javaType指定类型-->
        <collection property="students" ofType="Student">
            <id property="id" column="sid"/>
            <result property="name" column="sname"/>
        </collection>
    </resultMap>
    
    <select id="getTeacher"  resultMap="TeacherStudent">
        select t.id as tid, t.name as tname, s.id as sid, s.name as sname
        from teacher as t, student as s
        where t.id=#{tid} and t.id=s.tid;
    </select>
    
</mapper>

测试

@Test
public void test10() {
    SqlSession sqlSession = MybatisUtil.getSqlSession();

    TeacherMapper teacherDAO = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher = teacherDAO.getTeacher(1);
    System.out.println(teacher);

    sqlSession.close();
}

一对多,多对一小结

association:关联(多对一)
collection:集合(一对多)
javaType:指定对象类型
ofType:指定集合的泛型类型

动态SQL

官方文档:https://mybatis.org/mybatis-3/zh/dynamic-sql.html

IF

如果有name参数,通过单独查找指定用户,否则查找全部用户。

StudentMapper

List<Student> queryStudentIF(Map<String, Object> map);

StudentMapper.xml

<select id="queryStudentIF" parameterType="map" resultType="Student">
    select * from student where 1=1
    <if test="username != null">
         and name=#{username}
    </if>
</select>

1=1是为了where的合法性,如果where放到if标签里的话,如果有多个判断呢,一条sql语句通常只有一个where,所以放在外面,用1=1保证where合法,这样可以保证我们可以写多个if标签。

测试

@Test
public void test11() {
    SqlSession sqlSession = MybatisUtil.getSqlSession();

    StudentMapper studentDAO = sqlSession.getMapper(StudentMapper.class);
    Map<String, Object> map = new HashMap<>();
    // 不传参,查找全部
    // 传参,单独查找
    //map.put("username", "小刚");

    List<Student> students = studentDAO.queryStudentIF(map);
    for (Student student : students) {
        System.out.println(student);
    }

    sqlSession.close();
}

choose、when、otherwise

choose结构与Java中的switch类似,when对应Java的case,otherwise对应Java的default。

<select id="queryStudentIF" parameterType="map" resultType="Student">
    select * from student where
    <choose>
        <when test="sid != null">
            and id=#{sid}
        </when>
        <when test="username != null">
            and name=#{username}
        </when>
        <otherwise>
            1=1
        </otherwise>
    </choose>
</select>

when标签有一个返回即停止,不会再去判断其他when,如果所有的when都不符合,执行otherwise。

where

看下面的代码,这个代码存在一些问题,如果if都不符合,那么where语句后会没有判断条件,或者如果sid为空,但是username不为空,那么会变成... where and ...

<select id="queryStudentIF" parameterType="map" resultType="Student">
    select * from student where
    <if test="sid != null">
        id=#{sid}
    </if>
    <if test="username != null">
        and name=#{username}
    </if>
</select>

解决办法可以用上面的choose,也可以用where标签,where标签会在子标签有返回内容的情况下才会插入where关键字,并且where会判断如果拼接的第一条语句开头为and或or,会自动去除。

<select id="queryStudentIF" parameterType="map" resultType="Student">
    select * from student
    <where>
        <if test="sid != null">
            id=#{sid}
        </if>
        <if test="username != null">
            and name=#{username}
        </if>
    </where>
</select>

建议多使用where标签替代where关键字,因为where标签只有子标签有返回结果时才生效,省去一些if判断。

trim

如果对where不满意,可以使用trim进行自定义。

<!--这个trim作用与where标签一样,可以自行修改-->
<trim prefix="where" prefixOverrides="and |or ">
    <if test="sid != null">
        id=#{sid}
    </if>
    <if test="username != null">
        and name=#{username}
    </if>
</trim>

prefix:开头添加的内容,如同where标签添加的where关键字。
prefixOverrides:第一个拼接语句开头如果存在就要移除的内容,and和or后面的空格是必须的,因为sql语句中and和or关键字后就有空格。
suffix:结尾添加的内容。
suffixOverrides:最后一个拼接语句末尾如果存在就要移除的内容。

set

set标签对应sql语句update中的set关键字,用于修改数据时,根据要修改内容进行动态更新。

StudentMapper

void changeStudentSET(Map<String, Object> map);

StudentMapper.xml

<update id="changeStudentSET" parameterType="map">
    update student
    <set>
        <!--多个if判断,因为建的表字段少,所以这里就只用一个-->
        <if test="username != null">name=#{username},</if>
    </set>
    where id=#{sid};
</update>

测试

@Test
public void test12() {
    SqlSession sqlSession = MybatisUtil.getSqlSession();

    StudentMapper studentDAO = sqlSession.getMapper(StudentMapper.class);
    Map<String, Object> map = new HashMap<>();
    map.put("sid", "2");
    map.put("username", "小兰");

    studentDAO.changeStudentSET(map);

    sqlSession.commit();
    sqlSession.close();
}

set标签会去除拼接的sql语句最后多余的逗号,。

使用trim标签模拟set标签的功能:

<update id="changeStudentSET" parameterType="map">
    update student
    <trim prefix="set" suffixOverrides=",">
        <if test="username != null">name=#{username},</if>
    </trim>
    where id=#{sid};
</update>

foreach

使用foreach动态拼接成select * from student where 1=1 and (id=1 or id=2);

<select id="queryStudentFOREACH" parameterType="map" resultType="Student">
    select * from student
    <where>
        <foreach collection="ids" item="item" index="index" open="and (" separator=" or " close=")">
            id=#{item}
        </foreach>
    </where>
</select>

collection:遍历的集合
item:当前遍历到的元素
index:当前遍历到的下标
open:拼接到sql语句的开头的片段。
separator:遍历元素中间添加的分隔符。
close:拼接到sql语句的末尾的片段。

测试

@Test
public void test13() {
    SqlSession sqlSession = MybatisUtil.getSqlSession();

    StudentMapper studentDAO = sqlSession.getMapper(StudentMapper.class);
    Map<String, Object> map = new HashMap<>();
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    map.put("ids", list);

    List<Student> students = studentDAO.queryStudentFOREACH(map);
    students.forEach(System.out::println);

    sqlSession.close();
}

SQL片段

抽取重复使用的sql片段,与java中抽取重复代码封装成函数一样,减少代码量,方便复用,但也要注意,抽取的sql片段尽量简单,最好只放判断语句if或choose,如果太复杂使用的次数也相对较少。

<sql id="if-sid-username">
    <if test="sid != null">
        id=#{sid}
    </if>
    <if test="username != null">
        and name=#{username}
    </if>
</sql>

<select id="queryStudentIF" parameterType="map" resultType="Student">
    select * from student
    <where>
        <include refid="if-sid-username"/>
    </where>

</select>

缓存

官方文档:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache

缓存就是把查询到的数据存放到内存里,下次再进行相同的查询就不用通过数据库,直接从缓存(内存)中获取数据,这样可以减少跟数据库的交互,减少系统开销,提高效率,经常查询并且不经常修改的数据最合适用缓存。

Mybatis有一级缓存和二级缓存,默认开启一级缓存。
在这里插入图片描述
所有查询操作都会被保存到缓存中,并且所有的增删改操作都会刷新缓存,缓存不会自动刷新(定时刷新)。

一级缓存

一级缓存:同个SqlSession中,多次进行同一个的查询操作,只有第一次操作需要与数据库交互,并保存到缓存中,接下来的查询操作会直接从缓存中获取数据,不会与数据库交互,具体可以通过日志查看。

使用sqlSession.clearCache();也可以清空SqlSession的缓存。

一级缓存就是一个Map。

二级缓存

在mapper文件中添加<cache/>,代表此mapper文件使用二级缓存,默认的清除策略为LRU(移除最长时间没使用的对象),cache标签也可以自定义属性。
在这里插入图片描述

mybatis-config.xml

<settings>
    <!--表明我们要开启全局缓存,不加这个配置也可以,因为默认也是开启的,不过写出来方便别人知道我们用到了全局缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

使用缓存时,每个实体类都需要序列化(实现Serializable接口),否则会报错NotSerializableException,不想序列化的话,在cache标签设置readOnly属性为true即可,不过建议实体类都序列化。

每个mapper为都有一个二级缓存。每个SqlSession的查询操作后的数据会保存到一级缓存中,当mapper开启二级缓存后,当它的SqlSession提交或者关闭后,才会把一级缓存中的内容保存到二级缓存里,不关闭不会保存到二级缓存。

  • 没开启缓存时
    同个SqlSession同个mapper使用一级缓存
    不同个SqlSession同个mapper不能使用缓存

  • 开启缓存后
    同个SqlSession同个mapper先使用二级缓存,如果二级缓存中不存在,再使用一级缓存。
    不同个SqlSession同个mapper使用二级缓存

不同mapper不能使用缓存。

在开启二级缓存后,mapper文件中的select查询可以把useCache属性设置为false,表示当前查询不使用缓存,在增删改操作中可以把flushCache属性设置为false,表示当前操作不刷新缓存。

自定义缓存

实现Cache接口,然后在cache标签中type属性引用即可,目前大多数都是使用redis作为缓存。

学习视频:https://www.bilibili.com/video/BV1NE411Q7Nx?p=1

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis是一个开源的持久化框架,可以帮助我们将数据从数据库中读取出来,然后转换为Java对象,并将Java对象写入数据库中。 在C#中使用MyBatis,需要先安装MyBatis.Net库,然后在项目中引用该库。接着,我们需要创建一个配置文件,用于配置MyBatis的数据库连接信息、SQL语句等。在配置文件中,我们需要指定一个别名,用于在程序中引用这个配置文件。 接下来,我们需要创建一个映射文件,用于将数据库中的数据映射为Java对象。在映射文件中,我们需要定义一个 resultMap,用于定义Java对象与数据库表之间的关系。我们还需要定义一个 SQL 语句,用于从数据库中读取数据,并将其转换为Java对象。 在程序中,我们需要创建一个 SqlSession 对象,用于执行SQL语句。我们可以通过SqlSession对象调用selectOne、selectList、update、delete等方法,来执行SQL语句,并将结果转换为Java对象或者操作数据库。 下面是一个简单的示例,展示了如何在C#中使用MyBatis: 1. 安装MyBatis.Net库 在Visual Studio中,选择“工具”-“NuGet包管理器”-“程序包管理器控制台”,然后输入以下命令: ``` Install-Package MyBatisNet ``` 2. 创建配置文件 在项目中创建一个名为“SqlMapConfig.xml”的文件,用于配置数据库连接信息、SQL语句等。以下是一个示例配置文件: ``` xml <?xml version="1.0" encoding="utf-8" ?> <sqlMapConfig> <database> <provider name="SqlServer" connectionString="Data Source=localhost;Initial Catalog=mydatabase;User ID=myuser;Password=mypassword;" /> </database> <sqlMap> <map resource="MyMapper.xml"/> </sqlMap> </sqlMapConfig> ``` 其中,provider元素用于指定数据库类型和连接字符串,map元素用于指定映射文件路径。 3. 创建映射文件 在项目中创建一个名为“MyMapper.xml”的文件,用于将数据库中的数据映射为Java对象。以下是一个示例映射文件: ``` xml <?xml version="1.0" encoding="utf-8" ?> <sqlMap namespace="MyMapper"> <resultMap id="MyResultMap" class="MyClass"> <result property="id" column="id"/> <result property="name" column="name"/> </resultMap> <select id="selectById" resultMap="MyResultMap"> SELECT * FROM mytable WHERE id=#id# </select> </sqlMap> ``` 其中,resultMap元素用于定义Java对象与数据库表之间的关系,select元素用于定义SQL语句。 4. 在程序中使用MyBatis 在程序中,我们需要创建一个 SqlSession 对象,用于执行SQL语句。以下是一个示例代码: ``` csharp using IBatisNet.DataMapper; using IBatisNet.DataMapper.Configuration; using IBatisNet.DataMapper.Configuration.Files; // 创建配置文件 DomSqlMapBuilder builder = new DomSqlMapBuilder(); ISqlMapper sqlMapper = builder.Configure(@"SqlMapConfig.xml"); // 执行SQL语句 MyClass obj = sqlMapper.QueryForObject<MyClass>("MyMapper.selectById", new { id = 1 }); ``` 以上是一个简单的示例,展示了如何在C#中使用MyBatis。实际上,MyBatis还有很多其他的用法和功能,需要我们在实际开发中去探索和使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值