MyBatis 基本的 CRUD 操作及配置文件解析
下面介绍基于 xml 映射文件方式实现基本的 CRUD 操作和
Mybatis 的学习 demo 已上传 GitHub,地址:https://github.com/Jacks5320/MybatisStudyDemo
1 数据库表
CREATE DATABASE IF NOT EXISTS `mybatis_study`;
USE `mybatis_study`;
CREATE TABLE IF NOT EXISTS `tb_account`(
`id` INT(10) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(10) DEFAULT NULL,
`money` FLOAT(5.2) DEFAULT 0,
PRIMARY KEY( `id` )
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `tb_account`(`id`,`name`,`money`) VALUES
(1,'小张',1000.0),
(2,'小李',2000.0),
(3,'小丽',1500.0);
2 实体类
public class Account implements Serializable {
private Integer id;
private String accountName;
private Double nowMoney;
// 以下省略 set、get、toString 方法
}
3 核心配置文件
配置文件中不是所有的配置都用到了,其他标签暂时作扩展。
jdbc-confg.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_study?serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root
核心配置文件
<?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">
<!-- 跟标签,mybatis在 Configuration 类中定义了一些别名 -->
<configuration>
<!-- ======================== 读取外部配置文件 ========================= -->
<!-- 用于读取外部的 properties 配置文件 -->
<properties resource="jdbc-Config.properties"/>
<!-- ======================== 重要配置项 ========================= -->
<!-- 配置一些重要的配置项,如缓存、下划线命名规则转驼峰命名等 -->
<settings>
<!-- 下划线命名转驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- ======================== 起别名 ========================= -->
<!-- 给 pojo 类取别名,在 映射文件中引用 pojo 类型的时候就可以直接使用别名,不区分大小写 -->
<typeAliases>
<!-- 配置单个 pojo -->
<typeAlias type="com.pojo.Account" alias="Account"/>
<!-- 批量起别名,可能会有同名 pojo,可以在实体类上使用 @Alias 取其他别名 -->
<package name="com.pojo"/>
</typeAliases>
<!-- ======================== 数据源环境配置 ========================= -->
<!-- 可以配置多个数据源,default 可以指定使用哪个数据源,值为具体环境配置的 id -->
<!-- 通过 default 属性可以自由切换数据源 -->
<environments default="mysql">
<!-- 具体数据源配置,id 是该环境的唯一标识 -->
<environment id="mysql">
<!-- 事务管理类型 -->
<transactionManager type="JDBC"/>
<!-- jdbc连接信息,type 可取值:POOLED(采用池)、UNPOOLED(不采用池,每次访问新建连接)、JNDI(服务器容器,只能web项目用) -->
<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>
<!-- ======================== 数据库厂商标识 ========================= -->
<!-- type=DB_VENDOR ==> VendorDatabaseIdProvider -->
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
<!-- ======================== 映射器 ========================= -->
<!-- 注册 mapper,告诉 MyBatis 去哪里寻找 SQL 映射-->
<mappers>
<!-- 单个注册 -->
<!-- resource 类路径下的文件 -->
<!-- url,指定映射文件的 url,可以使用 file 协议指定本地 -->
<mapper resource="com/mapper/demo2/AccountMapper.xml"/>
<!-- 批量注册 -->
<!-- name,指定包名,要使用批量注入必须保证 Mapper 接口和 Mapper映射文件的目录结构和命名完全一致 -->
<!-- 例如:com.mapper.demo2.AccountMapper.java <==> com.mapper.demo2.Account.xml -->
<!-- 这样作可以让 Java 编译后在同一个文件目录下 -->
<!--<package name="com.mapper.demo2"/>-->
</mappers>
</configuration>
4 Mapper 接口
public interface AccountMapper {
// 查询所有
List<Account> findAll();
// 根据 ID 查询
Account findById(Integer id);
// 保存
void saveAccount(Account account);
// 更新
void updateAccount(Account account);
// 删除
void deleteById(Integer id);
// 模糊查询1 使用 ${} 拼接
List<Account> findByName(String name);
// 模糊查询2 使用 #{} 拼接
List<Account> findByName2(String name);
// 模糊查询3 使用 bind 标签拼接
List<Account> findByName3(String name);
}
5 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,用于指定对应 mapper 接口的全类名
-->
<mapper namespace="com.mapper.demo2.AccountMapper">
<!-- 自定义结果集映射,将 pojo 类与 数据库中的字段名不同的地方作映射 -->
<!-- 在核心配置文件中配置别名,所以可以直接使用 account ,不区分大小写-->
<!-- 但是更建议使用全类名,可以 ctrl + 左键进入接口查看方法,这里作为演示,就用 account 了 -->
<resultMap id="accountMap" type="account">
<!-- id指主键、result指普通属性 -->
<!-- column 指数据库字段名、property 指 pojo 属性名 -->
<id column="id" property="id"/>
<result column="name" property="accountName"/>
<result column="money" property="nowMoney"/>
</resultMap>
<!-- ===============查询所有===================== -->
<!-- 可以通过起别名,也可以通过自定义结果集映射来解决查询值为 null 的情况 -->
<!-- 需要注意,起的别名也必须与 Java 类的属性名一致,不然查到的也是 null -->
<select id="findAll" resultType="com.pojo.Account">
select id,name as accountName,money as nowMoney from tb_account
</select>
<!-- ===============根据 id 查询===================== -->
<!-- resultMap 指定自定义的结果集映射,值为自定义结果集的 id -->
<!-- #{} 用于获取传入的参数 -->
<!-- 如果传参只有一个 普通类型 或 String 参数,则命名可以随意 -->
<!-- 如果是一个 pojo 对象,可以用 OGNL 表示式获取 pojo 的属性,详情见保存方法 -->
<!-- #{} 是 PreparedStatement 中的占位符,可以将传入的单个参数放入占位符中 -->
<select id="findById" resultMap="accountMap">
select * from tb_account where id = #{accountId}
</select>
<!-- ===============保存方法===================== -->
<!-- 没有返回值 -->
<!-- 可以使用 selectKey 获取当前插入记录的 id 值 -->
<insert id="saveAccount" parameterType="com.pojo.Account">
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into tb_account(name,money) values (#{accountName},#{nowMoney})
</insert>
<!-- =============== 更新方法 ===================== -->
<update id="updateAccount" parameterType="com.pojo.Account">
update tb_account set money=#{nowMoney} where id = #{id}
</update>
<!-- =============== 删除方法 ===================== -->
<delete id="deleteById">
delete from tb_account where id = #{accountId}
</delete>
<!-- =============== 模糊查询1 ===================== -->
<!-- ${} 是 Statement 的字符串拼接,有 sql 注入问题,但是能替代 #{} 不能使用的位置 -->
<select id="findByName" resultMap="accountMap">
select * from tb_account where name like '%${accountName}%'
</select>
<!-- =============== 模糊查询2 ===================== -->
<select id="findByName2" resultMap="accountMap">
select * from tb_account where name like #{accountName}
</select>
<!-- =============== 模糊查询3 ===================== -->
<select id="findByName3" resultMap="accountMap">
<bind name="accountName" value="'%' + accountName + '%'"/>
select * from tb_account where name like #{accountName}
</select>
</mapper>
- 注意,编写好的 xml 映射文件一定要到核心配置文件中注册。
6 测试方法
public class Demo2Test {
InputStream in = null;
SqlSession sqlSession = null;
AccountMapper mapper = null;
@Before // 测试方法执行之前执行
public void init() throws Exception {
// 1 将配置文件读到字节输入流
in = Resources.getResourceAsStream("com/mapper/demo2/mybatis-config.xml");
// 2 使用构建者读取字节流,创建工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3 使用工厂创建 SqlSession 对象
sqlSession = factory.openSession();
// 4 使用 SqlSession 对象创建 Mapper 代理对象
mapper = sqlSession.getMapper(AccountMapper.class);
}
@After // 执行方法执行结束后执行
public void after() throws Exception {
// 6 释放资源
in.close();
}
// 1 测试查询所有
@Test
public void testFindAll() {
List<Account> accountList = mapper.findAll();
for (Account account : accountList) {
System.out.println(account);
}
}
// 2 根据 id 查询
@Test
public void testFindById(){
Account account = mapper.findById(1);
System.out.println(account);
}
// 3 新增一条记录
@Test
public void testSave(){
Account account = new Account();
account.setAccountName("李华");
System.out.println("提交前:" + account);
mapper.saveAccount(account);
// 提交事务!
sqlSession.commit();
// 获取到 key
System.out.println("提交后:" + account);
}
// 4 修改一条数据,修改新增数据的 money
@Test
public void testUpdate(){
Account account = new Account();
account.setId(4);
account.setNowMoney(10000.0);
mapper.updateAccount(account);
// 提交事务!
sqlSession.commit();
}
// 5 删除一条数据
@Test
public void testDelete(){
mapper.deleteById(4);
// 提交事务
sqlSession.commit();
}
// 6 模糊查询:可以再控制台看日志中发送的 sql 语句,区分 ${} 与 #{} 的区别
@Test
public void testFindByName(){
//模糊查询1:使用 ${}
List<Account> accountList1 = mapper.findByName("小");
for (Account account : accountList1){
System.out.println(account);
}
System.out.println(" ------- 分割线 -------- ");
//模糊查询2:使用 #{},在外面拼接好再传入,更灵活
List<Account> accountList2 = mapper.findByName2("%小%");
for (Account account : accountList2){
System.out.println(account);
}
System.out.println(" ------- 分割线 -------- ");
//模糊查询3:使用 #{} + bind 标签,可以避免 sql 注入
List<Account> accountList3 = mapper.findByName3("小");
for (Account account : accountList3){
System.out.println(account);
}
}
}
- 需要注意的是,增、删、改操作需要提交事务,也可以将事务提交放到 after 方法中,这里写出来只是为了加深记忆。
7 #{} 与 ${} 取值的区别
#{}
是PreparedStatement
中的参数占位符,将传入的参数放到占位符的位置。${}
是Statement
中的字符串拼接,将传入的参数已字符串的形式拼接到 SQL 中,如果不是数字类型,需要加上单引号。- 开发中更常用
#{}
取值,相对更安全,使用${}
取值,更容易引起 sql 注入问题。
- 这是测试模糊查询方法,第一个使用 ${} 取值,后两个使用 #{} 取值。