Mybatis基础
虽然我们能够通过JDBC来连接和操作数据库,但是哪怕只是完成一个SQL语句的执行,都需要编写大量的代码,更不用说如果我还需要进行实体类映射,将数据转换为我们可以直接操作的实体类型,JDBC很方便,但是还不够方便。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
XML语言概述
XML语言发明最初是用于数据的存储和传输
HTML主要用于通过编排来展示数据,而XML主要是存放数据,它更像是一个配置文件
<?xml version="1.0" encoding="UTF-8" ?> <outer> <name>阿伟</name> <desc>怎么又在玩电动啊</desc> <inner type="1"> <age>10</age> <sex>男</sex> </inner> </outer>
一个XML文件存在以下的格式规范:
- 必须存在一个根节点,将所有的子标签全部包含
- 可以但不必须包含一个头部声明(主要是可以设定编码格式)
- 所有的标签必须成对出现,可以嵌套但不能交叉嵌套
- 区分大小写
- 标签中可以存在属性,属性的值由单引号或双引号包括
XML文件也可以使用注释:
<?xml version="1.0" encoding="UTF-8" ?> <!-- 注释内容 -->
内容中出现特定字符需要使用转义字符:
使用CD来快速创建不解析区域:
<test> <name><![CDATA[我看你<><><>是一点都不懂哦>>>]]></name> </test>
使用Mybatis
导入Mybatis的依赖
编写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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${驱动类(含包名)}"/> <property name="url" value="${数据库连接URL}"/> <property name="username" value="${用户名}"/> <property name="password" value="${密码}"/> </dataSource> </environment> </environments> </configuration>
通过进行配置告诉了Mybatis我们链接数据库的一些信息,包括URL、用户名、密码等
Mybatis对配置文件进行读取并得到一个SqlSessionFactory
对象:
public static void main(String[] args) throws FileNotFoundException { SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("mybatis-config.xml")); try (SqlSession sqlSession = sqlSessionFactory.openSession(true)){ //暂时还没有业务 } }
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的,可以通过SqlSessionFactory
来创建多个新的会话
配合lombok编写实体类:
import lombok.Data; @Data public class Student { int sid; //名称最好和数据库字段名称保持一致,不然可能会映射失败导致查询结果丢失 String name; String sex; }
在根目录下重新创建对象映射器文件TestMapper.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="TestMapper"> <select id="selectStudent" resultType="com.test.entity.Student"> select * from student </select> </mapper>
其中namespace就是命名空间,每个Mapper都是唯一的,因此需要用一个命名空间来区分,它还可以用来绑定一个接口。
写入了一个select标签,表示添加一个select操作,同时id作为操作的名称,resultType指定为我们刚刚定义的实体类,表示将数据库结果映射为Student类,然后就在标签中写入我们的查询语句即可。
在配置文件中添加这个Mapper映射器:
<mappers> <mapper url="file:mappers/TestMapper.xml"/> <!-- 这里用的是url,也可以使用其他类型 --> </mappers>
在程序中使用定义好的Mapper:
public static void main(String[] args) throws FileNotFoundException { SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("mybatis-config.xml")); try (SqlSession sqlSession = sqlSessionFactory.openSession(true)){ List<Student> student = sqlSession.selectList("selectStudent"); student.forEach(System.out::println); } }
Mybatis非常智能,只需要告诉一个映射关系,就能够直接将查询结果转化为一个实体类
配置Mybatis
由于SqlSessionFactory
一般只需要创建一次,因此我们可以创建一个工具类来集中创建SqlSession
,这样会更加方便一些:
public class Mybatis { private static SqlSessionFactory sqlSessionFactory; static { try { sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("mybatis-config.xml")); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } public static SqlSession getSession(boolean autoCommit) { return sqlSessionFactory.openSession(autoCommit); } }
public static void main(String[] args) { try (SqlSession sqlSession = MybatisUtil.getSession(true)){ List<Student> student = sqlSession.selectList("selectStudent"); student.forEach(System.out::println); } }
每次都需要去找映射器对应操作的名称,而且还要知道对应的返回类型,再通过SqlSession
来执行对应的方法,能不能再方便一点呢?
通过namespace
来绑定到一个接口上,利用接口的特性,我们可以直接指明方法的行为,而实际实现则是由Mybatis来完成。
public interface TestMapper { List<Student> selectStudent(); }
将Mapper文件的命名空间修改为我们的接口,建议同时将其放到同名包中,作为内部资源:
<mapper namespace="com.test.mapper.TestMapper"> <select id="selectStudent" resultType="com.test.entity.Student"> select * from student </select> </mapper>
修改一下配置文件中的mapper定义,不使用url而是resource表示是Jar内部的文件:
<mappers> <mapper resource="com/test/mapper/TestMapper.xml"/> </mappers>
可以直接通过SqlSession
获取对应的实现类,通过接口中定义的行为来直接获取结果:
public static void main(String[] args) throws FileNotFoundException { try(SqlSession session = Mybatis.getSession(true)) {//自动提交 TestMapper testMapper = session.getMapper(TestMapper.class); List<Student> selectStudent = testMapper.selectStudent(); selectStudent.forEach(System.out::println); } }
TestMapper是通过动态代理生成的,相当于动态生成了一个实现类,而不是预先定义好的
配置文件介绍:
<configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/study"/> <property name="username" value="test"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/test/mapper/TestMapper.xml"/> </mappers> </configuration>
environments
标签指定一个数据库的配置信息,包含连接URL、用户、密码等信息
实际情况下可能会不止有一个数据库连接信息,比如开发过程中我们一般会使用本地的数据库,而如果需要将项目上传到服务器或是防止其他人的电脑上运行时,我们可能就需要配置另一个数据库的信息
在environments
标签上有一个default属性,来指定默认的环境
也可以在创建工厂时选择环境:
sqlSessionFactory = new SqlSessionFactoryBuilder() .build(new FileInputStream("mybatis-config.xml"), "环境ID");
可以给类型起一个别名:
<!-- 需要在environments的上方 --> <typeAliases> <typeAlias type="com.test.entity.Student" alias="Student"/> </typeAliases>
直接让Mybatis去扫描一个包,并将包下的所有类自动起别名(别名为首字母大小写都行的类名):
<typeAliases> <package name="com.test.entity"/> </typeAliases>
也可以为指定实体类添加一个注解,来指定别名:
@Data @Alias("lbwnb") public class Student { private int sid; private String name; private String sex; }
增删改查
resultType可以被映射到一个Map上:
<select id="selectStudent" resultType="Map"> select * from student </select>
public interface TestMapper { List<Map> selectStudent(); }
Map中就会以键值对的形式来存放这些结果了
通过设定一个resultType
属性,让Mybatis知道查询结果需要映射为哪个实体类,要求字段名称保持一致。
自定义resultMap
来设定映射规则:
<resultMap id="Test" type="Student"> <result column="id" property="id"/> <result column="name" property="name"/> </resultMap> <select id="selectStudent" resultMap="Test"> select * from student </select>
column表示数据库字段名称,property表示实体类字段名称
如果一个类中存在多个构造方法,那么很有可能会出现错误
使用constructor
标签来指定构造方法:
<resultMap id="test" type="Student"> <constructor> <arg column="sid" javaType="Integer"/> <arg column="name" javaType="String"/> </constructor> </resultMap>
指定构造方法后,若此字段被填入了构造方法作为参数,将不会通过反射给字段单独赋值,而构造方法中没有传入的字段,依然会被反射赋值
如果只有一个构造函数或者全量构造函数都会先调用一遍构造函数,然后使用反射进行字段单独赋值
数据库中存在一个带下划线的字段,我们可以通过设置让其映射为以驼峰命名的字段,比如my_test
映射为myTest
:
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
条件查询:想通过sid字段来通过学号查找信息
Student getStudentBySid(int sid);
<select id="getStudentBySid" parameterType="int" resultType="Student"> select * from student where sid = #{sid} </select>
通过使用#{xxx}
或是${xxx}
来填入我们给定的属性,实际上Mybatis本质也是通过PreparedStatement
首先进行一次预编译,有效地防止SQL注入问题,但是如果使用${xxx}
就不再是通过预编译,而是直接传值,因此我们一般都使用#{xxx}
来进行操作。
使用parameterType
属性来指定参数类型(非必须,可以不用,推荐不用)
插入、更新和删除操作:
<insert id="addStudent" parameterType="Student"> insert into student(name, sex) values(#{name}, #{sex}) </insert>
int addStudent(Student student);