MyBatis 入门(一)

时间:2023-03-09 15:39:27
MyBatis 入门(一)

1. MyBatis 概述

  1. MyBatis 是一个半自动化的持久层框架;
    • 核心SQL,开发人员可以进行优化;
    • SQL和Java编码分开,功能边界清晰,一个专注业务,一个专注数据;
  2. JDBC:
    • SQL 语句夹在Java代码块里,耦合度高,导致硬编码内伤;
    • 维护不易且实际开发需求中SQL是有变化,频繁修改的情况多见;
  3. Hibernate 和 JPA
    • 长难复杂SQL,对于Hibernate而言处理也不容易;
    • 内部自动生产的SQL,不容易做特殊优化;
    • 基于全映射的全自动框架,大量字段的POJO进行部分映射时,比较困难;从而导致数据库性能下降;

2. MyBatis 入门程序搭建

// 创建 Java Project

// 1. 导入 jar 包(3个)
/*
* log4j-1.2.17(用来在控制台打印 SQL 语句)
* mybatis-3.4.1
* mysql-connector-java (SQL 驱动包)
*/ // 2. 创建 Bean
// cn.itcast.mybatis.bean.Employee.java
public class Employee{
private Integer id;
private String lastName;
private String email;
// 以数字 0 表示男性, 1 表示女性
private Integer gender; // getter 和 setter 略
} // 3. 配置文件, 存放在 conf 文件夹下
// 创建 Source Folder, 名称为 conf // log4j.xml
(具体见"参考资料"中链接) // 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="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments> <!-- 将写好的SQL映射文件,一定要注册到全局配置文件中 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration> // EmployeeMapper.xml
// sql 映射文件
<?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="cn.itcast.mybatis.EmployeeMapper"> <!-- namespace: 命名空间, id: 表示唯一标识, resultType: 返回值类型 -->
<select id="selectEmp" resultType="cn.itcast.mybatis.bean.Employee"> <!-- 数据库中 last_name, Bean 类中 lastName, 以下相当于用别名查询 -->
<!-- #{id} 表示从传递过来的参数中,获取id值 -->
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper> // 4. 编写测试类
public class MyBatisTest{ // 1. 根据mybatis的配置文件(全局配置文件),创建一个 SqlSessionFactory 对象; @Test
public void test() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resorces.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream); // 2. 获取 SqlSession 实例, 使用该实例执行数据库的增删改查;
// 一个sqlSession,就是代表和数据库的一次会话,用完需要关闭;
// sqlSession 是非线程安全的,每次使用都应该去获取新的对象;
SqlSession openSession = sqlSessionFactory.openSession(); try{
// selectOne(String statement, Object parameter) 参数说明:
// statement: sql的唯一标识, 建议写法: 命名空间+id
// parameter: 执行 sql 要用的参数
Employee employee =
openSession.selectOne("cn.itcast.mybatis.EmployeeMapper.selectEmp",1);
System.out.println(employee);
}finally{
openSession.close();
}
}
} // 升级版本: 接口式编程
// 使用接口,实现增删改查
// 创建 cn.itcast.mybatis.dao.EmployeeMapper.java 接口
public interface EmployeeMapper{ // 按id查询
public Employee getEmpById(Integer id);
} // 修改 EmployeeMapper.xml
// sql 映射文件
<?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="cn.itcast.mybatis.dao.EmployeeMapper"> <!--
namespace: 命名空间,指定为接口的全类名;
id: 表示唯一标识,接口中的方法名;
resultType: 返回值类型
--> <select id="getEmpById" resultType="cn.itcast.mybatis.bean.Employee"> <!-- 数据库中 last_name, Bean 类中 lastName, 以下相当于用别名查询 -->
<!-- #{id} 表示从传递过来的参数中,获取id值 -->
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper> // 修改测试类
public class MyBatisTest{ // 获取 sqlSessionFactory 对象的方法
public SqlSessionFactory getSqlSessionFactory() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
} @Test
public test02() throws IOException{
// 1. 获取 sqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); // 2. 获取 SqlSession 实例
SqlSession openSession = sqlSessionFactory.openSession(); try{
// 3. 获取接口的实现类对象
// mybatis 会为接口自动创建一个代理对象, 代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); // 调用接口实现类的方法
Employee employee = mapper.getEmpById(1);
System.out.println(employee);
}finally{
// 4. 关闭 SqlSession
openSession.close();
}
}
}

3. MyBatis 全局配置文件

3.1 常用标签介绍

  • <properties>: 引入外部 properties 配置文件的内容;
    • resource属性: 引入类路径下的资源;
    • url属性: 引入网络路径或者磁盘路径下的资源;
  • <settings>: 包含了很多重要的设置项
  • <typeAliases>: 别名处理器,可以给 java 类型起别名,方便使用;
  • <typeHandlers>: 类型处理器, 处理数据库类型与Java类型的转换;
  • <plugins>
  • <environments>: mybatis 可以配置多种环境, default 的值对应<environment>中的 id 值;
  • <databaseIdProvider>: 设置数据库厂商;
  • <mappers>: 将sql映射文件注册到全局配置文件中;
// 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> <properties resource="dbconfig.properties"></properties> <!-- 是否开启驼峰命名:即从数据库列名"a_column"到Java属性名 "aColumn" 的类似映射 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings> <!-- typeAliase, 别名处理器, 别名不区分大小写 -->
<typeAliases>
<!-- 默认别名就是类名小写: employee; 也可以使用 alias 指定新的别名 -->
<typeAlias type="cn.itcast.mybatis.bean.Employee"/> <!-- package: 为某个包下的所有类批量起别名
name: 指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写)) 批量起别名的情况下, 使用 @Alias 注解为某个类型指定新的别名;
-->
<package name="cn.itcast.mybatis.bean"/>
</typeAliases> <environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<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>
</environment>
</environments> <!-- 将写好的SQL映射文件,一定要注册到全局配置文件中
注册配置文件:
resource: 引入类路径下的sql映射文件;
url: 引用网络路径或者磁盘路径下的sql映射文件;
也可以注册接口:
class: 注册接口
1. 有 sql 映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
2. 没有 sql 映射文件,所有的 sql 都是利用注解写在接口上;
备注:
比较重要,复杂的 Dao 接口,使用 sql 映射文件;
简单的Dao接口为了开发方便,可以使用注解;
-->
<mappers>
<mapper resource="EmployeeMapper.xml" />
<mapper class="cn.itcast.mybatis.bean.EmployeeMapper"/>
</mappers>
</configuration> // dbconfig.properties 配置文件
jdbc.driver=com.mysql.jdbc.driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

4. MyBatis 映射文件

// 映射增删改查语句

// EmployeeMapper.java 接口
public interface EmployeeMapper{ // 单个参数
public Employee getEmpById(Integer id); public void addEmp(Employee employee); public void updateEmp(Employee employee); public void deleteEmpById(Integer id); // 备注:
// MyBatis 允许增删改直接定义以下返回值: Integer, Long, Boolean, void
// 表示增删改影响的行数, 如果行数大于0, Boolean 值为 true;
// 例如: public boolean updateEmp(Employee employee);
} // EmployeeMapper.xml 配置文件
<mapper namespace="cn.itcast.mybatis.dao.EmployeeMapper"> <!-- 查询方法 -->
<select id="getEmpById" resultType="cn.itcast.mybatis.bean.Employee">
select * from tbl_employee where id="#{id}"
</select> <!-- 添加方法
自增主键值的获取: MyBatis 也是利用 statement.getGeneratedKeys(); 不过,需要配置
useGeneratedKeys="true": 表示使用自增主键获取主键值策略
keyProperty: 指定对应的主键属性,也就是MyBatis获取到主键值以后,将这个值封装给 JavaBean 的哪个属性
-->
<insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(last_name,email,gender)
values(#{lastName},#{email},#{gender})
</insert> <!--
Oracle 不支持自增; Oracle 使用序列来模拟自增;
每次插入的数据的主键,是从序列中拿到的值,
Oracle 数据库中获取主键的配置:
-->
<insert id="addEmp" databaseId="oracle">
<!--
keyProperty: 查出的主键值封装给 JavaBean 的哪个属性
order="BEFORE": 当前sql在插入sql之前运行
resultType: 查询出数据的返回值类型
-->
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
<!-- 查询主键的sql语句 -->
select EMPLOYEES_SEQ.nextval from dual
</selectKey> <!-- 插入数据时的主键,是从序列中拿到的 -->
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(#{id},#{lastName},#{email})
</insert> <!-- 更新方法 -->
<update id="updateEmp">
update tbl_employee
set last_name=#{lastName},email=#{email},gender=#{gender}
where id=#{id}
</update> <!-- 删除方法 -->
<delete id="deleteEmpById">
delete from tbl_employee where id=#{id}
</delete> </mapper> // 编写测试类
public class MyBatisTest{
// 1. 获取 SqlSessionFactory 方法(同上) // 2. 测试方法
@Test
public void test01() throws IOException{
// 1. 获取 SqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); // 2. 获取 SqlSession 实例
// sqlSessionFactory.openSession(); 该实例需要手动提交数据
// sqlSessionFactory.openSession(true); 该实例可以自动提交数据
SqlSession openSession = sqlSessionFactory.openSession(); try{
// 3. 获取接口的实现类对象
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); // 添加方法
Employee employee = new Employee(null,"zhangsan","zhangsan@163.com","1");
mapper.addEmp(employee);
// 输出主键值
System.out.println(employee.getId()); // 更新方法
Employee employee = new Employee(1,"lisi","lisi@163.com","1");
mapper.updateEmp(employee); // 删除方法
mapper.deleteEmpById(2); // 4. 手动提交
openSession.commit();
}finally{
// 5. 关闭资源
openSession.close();
}
}
}

4.1 MyBatis 映射文件的参数处理

// 示例: 查询方法中带有多个参数

// EmployeeMapper.java 接口
public interface EmployeeMapper{ // 多个参数
public Employee getEmpByIdAndLastName(Integer id, String lastName);
} // EmployeeMapper.xml 配置文件
<mapper namespace="cn.itcast.mybatis.dao.EmployeeMapper"> <!-- 查询方法(多个参数)
出现异常: org.apache.ibatis.binding.BindingException:
Parameter 'id' not found. Available parameters are [0, 1, param1, param2]
-->
<select id="getEmpByIdAndLastName" resultType="cn.itcast.mybatis.bean.Employee">
select * from tbl_employee where id=#{id} and last_name=#{lastName}
</select> <!-- 查询方法(多个参数)
多个参数: mybatis 会做特殊处理
多个参数会被封装成一个 map,
其中 key: param1,...paramN, 或者参数的索引
value: 传入的参数值
#{}就是从 map 中获取指定的key值
-->
<select id="getEmpByIdAndLastName" resultType="cn.itcast.mybatis.bean.Employee">
select * from tbl_employee where id=#{param1} and last_name=#{param2}
</select> <!-- 查询方法(多个参数)
也可以使用命名参数的方法: 在封装参数时,明确指定map中的key值:
接口的写法:
public Employee getEmpByIdAndLastName(@Param("id")Integer id,
@Param("lastName")String lastName)
-->
<select id="getEmpByIdAndLastName" resultType="cn.itcast.mybatis.bean.Employee">
select * from tbl_employee where id=#{id} and last_name=#{lastName}
</select> <!-- 查询方法(多个参数)
POJO:
如果多个参数正好是业务逻辑的数据模型,可以直接传入POJO;
#{属性名}: 取出传入的POJO类的属性值 Map:
如果多个参数不是业务模型中的数据,没有对应的POJO,不经常使用, 为了方便,也可以传入Map
#{key}: 取出map中对应的值
此时, 接口中的方法:
public Employee getEmpByMap(Map<String, Object> map); TO:
如果多个参数不是业务模型的数据,但是经常要使用,推荐编写一个 TO(Tranfer Object)数据传输对象 特别注意: 如果是 Collection(List,Set)类型或者是数组,mybatis 也会特殊处理,把传入的list或
者数组封装到map中,
其中,key: Collection(collection), 如果是 List, 还可以使用 key(list),数组(array)
public Employee getEmpById(List<Integer> ids);
取出list集合中第一个id的值: #{list[0]}
-->
<select id="getEmpByMap" resultType="cn.itcast.mybatis.bean.Employee">
select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select> </mapper> // 编写测试类
public class MyBatisTest{
// 1. 获取 SqlSessionFactory 方法(同上) // 2. 测试方法
@Test
public void test01() throws IOException{
// 1. 获取 SqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); // 2. 获取 SqlSession 实例
// sqlSessionFactory.openSession(); 该实例需要手动提交数据
// sqlSessionFactory.openSession(true); 该实例可以自动提交数据
SqlSession openSession = sqlSessionFactory.openSession(); try{
// 3. 获取接口的实现类对象
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); // 4. 查询数据
Map<String,Object> map = new HashMap<String,Object>();
map.put("id",1);
map.put("lastName","zhangsan");
Employee employee = mapper.getEmpByMap(map);
System.out.println(employee); }finally{
// 5. 关闭资源
openSession.close();
}
}
}

4.2 参数值的获取

  1. #{}: 可以获取map中的值或者pojo对象属性的值;
  2. ${}: 也可以获取map中的值或者pojo对象属性的值;
  3. 区别:
    • #{}: 是以预编译的形式,将参数设置到sql语句中, PreparedStatement: 可以防止 Sql 注入;
    • ${}: 取出的值直接拼装在sql语句中, 会有安全问题;
    • 大多情况下,应该使用 #{};

MyBatis 入门(一)

4.3 #{}更丰富的用法

  • 参数位置支持的属性: javaType, jdbcType, mode(存储过程), numericScale, resultMap, typeHandler,

    jdbcTypeName;
  • 实际上,通常被设置的是: 可能为空的列名指定 jdbcType;因为mybatis对所有null都映射的是原生Jdbc的 OTHER;

    Oracle 数据库不能正确识别,会报错;
  • #{email,jdbcType=NULL}表示email列为null时,该列的类型为原生Jdbc的 NULL类型;

参考资料