开篇
好久不见,距离上篇文章的完成已经过了 n + 1 天了,感觉过的太久了。实际上这几天我都在闭关修炼,现在已经刚刚筑基成功,我就迫不及待的想要上来表演一翻了。
上篇我们写了利用 Java 代码操作数据库的方式,是不是比单纯的命令行操作数据库简单一点?修炼了这么多天,今天我们来学习更高的科技,利用比较出名的持久层框架 mybatis 来操作数据库。关于 mybatis 这个框架的说明,我想大部分人都知道,在这里我就不赘述了。咱们都是明白人,直接打开天窗说亮话。在开始之前,我们还是回忆一下上一篇文章中利用 jdbc 操作数据库的一些步骤。
- 加载数据库驱动
- 创建并获取数据库连接
- 创建 jdbc statement 对象
- 设置 sql 语句
- 设置 sql 语句中的参数(使用 PreparedStatement)
- 通过 PreparedStatement 执行 sql 语句,并返回结果
- 对 sql 执行的结果进行处理
- 释放资源(Connection PreparedStatement 以及 ResultSet)
大致就是这几个步骤,如果不能准确的回忆起来,请自行面壁。
那么肯定有同学有这样的疑问。既然 jdbc 能够操作数据库,那么为什么还要开发出像 hibernate 和 mybatis 这样的持久层框架呢?既然有了替代 jdbc 的框架,那么就说明 jdbc 确实是存在一定的弊端。
- 首先,jdbc 比较麻烦,这个是肯定的。
其次数据库频繁的创建,释放数据库连接会直接造成数据库性能的浪费。
再就是 sql 语句只能被硬编码在代码中,不利于扩展和维护,在实际应用中 sql 的变化可能是较大的,这样就导致了 sql 变动需要改变 java 代码。
不论你的数据库水平怎么样,至少以上的问题是显而易见的。
实操 MyBatis 前的准备
每一个框架在正式使用的时候,都需要准备一大堆东西,正所谓磨刀不误砍柴工,我们的 mybatis 也是这样的。下面我们需要几个步骤来为我们 mybatis 操作数据库做准备。【温馨提示:此准备工作有点长,可能引起身体不适,请各位耐心看完】
1. Mybatis 的下载
这个就不说了,这个框架被托管在 github 上,如果不知道怎么去下载的,请出门左转百度或者 google 。如果知道怎么下载的,请出门右转 github。
2. Mybatis 入门
任何框架的第一步,都应该是先下载框架,下载这步我们已经直接略过了,下载 mybatis 解压后,你们可能会看到这样的文件夹结构。
以 pdf 结尾的自然就是帮助文档,这份帮助文档其实写的已经很清楚了。别 BB 了,我知道你在想什么,不好意思,英文的,想看也得看,不想看也得看。
以 jar 结尾的自然就是 mybatis 的核心包了。至于 lib 目录,就是 mybatis 核心包所需要的一些依赖。
下面我们就正式开始 mybatis 的基础入门了。
1. 创建 mybatis 工程
在 IDE 中,新建一个 java 工程,并把 mybatis-3.4.4.jar 和 lib 目录中的全部 jar 包拷贝到工程中。我这里用的是 intellij,你用 eclipse 也行,开发工具无所谓,用的顺手就行。
既然是操作数据库,就必然有数据库的驱动包,所以我们还需要一个数据库的驱动包。
所有包的截图如下。
2. 加入日志文件 log4j.properties
所有框架都有日志文件,这里的 mybatis 便是用的 apache 旗下的开源日志框架 log4j。如果你仔细点,你会发现 mybatis 的依赖包中已经包含了 log4j 的 jar 包。
log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.ConversionPattern=%5p [%t] -%m%n
万年不变的代码,照着抄就行。
3. 配置数据库的基础信息
既然要操作数据库,我们就必须要知道数据库的一些信息,例如用户名,密码,驱动,链接等等信息。在 src 目录下,新建一个 db.properties 文件,这个文件就包含了我们数据库的所有信息。
代码我也给出来,如下
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis01?characterEncoding=utf-8
jdbc.username=root
jdbc.password=student
记得改一下数据库的密码和数据库信息,别只知道 ctrl + c,ctrl + v。
4. 配置 mybatis
每一个框架都有一个属于他自己的核心配置文件,没错,mybatis 也要一个。我们这里取名为 SqlMapConfig.xml,切记,放在 src 目录下。上面我们已经配置了数据库的信息,下面我们便要在核心配置文件中引入数据库信息。
上面的核心配置文件中只是引入了数据库的配置和连接池以及事务管理,其他的配置我们将在下面的工程中慢慢添加。这里的配置基本上是一个极简的模板。
SqlMapConfig.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="db.properties"/>
<environments default="development">
<environment id="development">
<!-- mybatis 事务管理器-->
<transactionManager type="JDBC"/>
<!-- mybatis 连接池-->
<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>
</configuration>
5. 编写 po 类
User.java
public class User{
private Integer uid;
private String name;
private String sex;
private String address;
//省略了 get 和 set 方法
}
6. 编写 sql 映射文件 user.xml
在 User.java 同目录下创建 sql 映射文件 user.xml,切记一定要在同一目录下。
user.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">
<!-- namespace 起到做sql 隔离的作用-->
<mapper namespace="test">
</mapper>
注意,这里的 namespace 只是起到了 sql 隔离的作用,放置重名,就像 java 中的包一样。
7. 加载 sql 映射文件
有了 sql 映射文件,接下来我们就需要加载我们的 sql 映射文件,在我们的核心配置文件中,我们通过 mapper 标签中的 resource 来引入一个映射文件。
<mapper resource="com/student/mybatis/bean/User.xml"/>
机智如你,你肯定想到了,如果有多个标签,不说上百个,就说五十个,我们这句话就要写 50 次,太浪费空间了。所以我们还有一个更加简便的方式,可以一次性加载多个 sql 映射文件。
<!--批量引入mapper文件 -->
<package name="com.student.mybatis.mapper"/>
这个语句是引入这个包下面的全部 sql 映射文件。
至此我们的准备工作就已经完成了,有的小伙伴应该迫不及待的想要脱裤子上去干了。
Mybatis 单表的增删改查
1. Mybatis 的查询
Mybatis 底层实际上还是封装的 jdbc,他只是对 jdbc 做了封装,所以如果要学好 mybatis ,前提还是要学好数据库的相关知识。
由于查询是最多的一部分,那么我们就首先从查询说起。
功能:根据 id 查找用户对象
sql 语句: select * from user where uid = 1;
在 mybatis 中实现查询的标签 select
select 标签中还是写的是 sql 语句。所以会写 sql 语句很重要!
parameterType:定义输入到 sql 中的映射类型。
resultType: 定义结果类型。
举个例子,从上面我们可以知道,我们需要传入的值为用户 id ,此 id 我们在 User.java 中定义的是 Integer 类型,所以我们传入的参数类型就是 java.lang.Integer,(PS:这里我们可以直接写成 int,mybatis 给我们默认提供了一些别名,例如 string 就可以代表 java.lang.String)查询完成之后,必然会返回一个或者多个 User 对象,所以这里的结果类型(resultType)即是 User.java 的全路径名称。当然这里我们也可以起别名。在 mybatis 的核心配置文件中,加入如下代码:
<!-- 配置自定义别名-->
<typeAliases>
<!-- 给 User 类定义别名为 user-->
<typeAlias type="com.student.mybatis.bean.User" alias="user"/>
</typeAliases>
如果有很多类,同样也可以批量定义别名
<!-- 配置自定义别名-->
<typeAliases>
<package name="com.student.mybatis.bean"/>
</typeAliases>
配置上面的批量定义别名后,该包下的所有实体类的别名都是和类名一样,你甚至可以大小写混合,它并不会报错,不过作为一名 Java 开发程序员,我们还是遵循一下 Java 的命名规范。
第一个单词首字母小写,其他的每个单词首字母都大写。
如果该包下面有 User 和 Order 两个实体类,那么他们的别名分别是 user 以及 order。
定义别名后的 select 语句
<select id="findUserById" parameterType="int" resultType="user">
select * from user where uid = #{uid}
</select>
编写测试类 TestMyBaits.java
public class TestMyBatis {
private SqlSessionFactory factory;
/**
* 在含有 @Test 标签的方法执行之前执行
* 加载 myBatis 的配置文件
* @throws Exception
*/
@Before
public void setUP() throws Exception{
String resources = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
factory = new SqlSessionFactoryBuilder().build(inputStream);
}
/**
* 根据 id 查询学生列表
*/
@Test
public void testFindUserById() {
SqlSession sqlSession = factory.openSession();
//selectOne有两个参数,第一个是我们在 sql 映射文件中定义的 namespace + select 标签中的id
//这样是为了唯一标识这个 sql 语句
User user = sqlSession.selectOne("test.findUserById", 1);
sqlSession.commit();
System.out.println(user);
}
}
执行结果
可以看到第一个“==>”之后有我们的 sql 语句,这就说明了 mybatis 这个框架底层实际上还是使用 JDBC 实现的。我们上面的 select 标签中的 #{id} 被替换成了 ?,说明我们的 #{id} 仅仅只是一个占位符而已,并没有其他的作用。sql 语句中还有另外一个符号 ${name},这个是拼接符,我们一般在做模糊查询的时候才会用到,至于这两个有什么区别,我们后面在来讲。
功能:根据名称模糊查询用户信息
sql 语句: select * from user where name like '%花%'
<select id="findUserByName" parameterType="string" resultType="user">
select * from user where name LIKE '%${value}%'
</select>
测试方法
@Test
public void testFindUserByName(){
SqlSession session = factory.openSession();
List<User> userList = session.selectList("test.findUserByName", "花");
for (User user :
userList) {
System.out.println(user);
}
}
运行截图
这里由于是模糊查询,所以我们在这里使用了拼接符号 ${xxx}。
2. Mybatis 的删除
有了上面的基础,我们的删除以及更新就变的很简单了。
<delete id="deleteUserById" parameterType="int">
delete from user where uid = #{uid}
</delete>
测试方法
@Test
public void testDeleteUser(){
SqlSession session = factory.openSession();
session.delete("test.deleteUserById", 5);
session.commit();
}
运行截图
3. Mybatis 的修改
sql 映射文件
<update id="updateUserById" parameterType="user">
update user set name = #{name} where uid = #{uid};
</update>
测试方法
@Test
public void testUpdateUser(){
SqlSession session = factory.openSession();
User user = new User();
user.setName("李春花");
user.setUid(6);
session.update("test.updateUserById", user);
session.commit();
}
运行结果
4. Mybatis 插入用户
sql 映射文件
<insert id="insertUser" parameterType="user">
insert into user(name, sex, address) values (#{name}, #{sex}, #{address});
</insert>
编写测试方法
@Test
public void testInsertUser(){
SqlSession session = factory.openSession();
User user = new User();
user.setName("李春花");
user.setSex("0");
user.setAddress("河北太原石家庄");
session.insert("test.insertUser", user);
session.commit();
}
执行结果
这里我们并没有设置 uid 的值,因为我们设置 uid 为主键,且自增长,所以我们并不需要设置主键的值,MySQL 数据库会自动帮我们添加值,但是如果我们使用的是 oracle 数据库,oracle 数据库并没有自增长主键这个功能,所以我们便需要手动设置 uid 的值。但是 uid 我们不能凭空手动创建出来,所以我们得依靠数据库去帮我们完成这个功能。利用数据库函数返回主键的值。
如果是自增长主键,则使用 select LAST_INSERT_ID() 这个方法会返回自增长主键的值。
如果是 uuid 作为主键,则使用 select uuid() 这个方法,这个方法会返回一个 32 位的唯一字符串。
更改 insert 标签中 sql 片段,如下。
<insert id="insertUser" parameterType="com.student.mybatis.bean.User">
<!-- select key 可以在需要的时候,将主键返回-->
<selectKey keyProperty="uid" resultType="java.lang.Integer" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into user(name, sex, address) values (#{name}, #{sex}, #{address});
</insert>
keyProperty:返回的主键存储在 pojo 类中的哪个属性
order:selectKey 的执行顺序,是相对于 insert 语句来说的,由于 mysql 的自增原理,执行完 insert 语句后才将主键生成,所以这里的 selectKey 的执行顺序为 after
resultType: 返回的主键是什么类型。
last_insert_id():是 MySQL 的函数,返回 auto_increment 自增列新纪录的 id 值。
使用 uuid 作为主键,则 insert 标签中的 sql 片段如下。
<insert id="insertUser" parameterType="com.student.mybatis.bean.User">
<selectKey keyProperty="uid" resultType="string" order="BEFORE">
SELECT uuid()
</selectKey>
insert into user(name, sex, address) values (#{name}, #{sex}, #{address});
</insert>
注意: uuid 的 顺序是 before。
5. 小结
至此,mybatis 的入门增删改查就已经全部完成了,现在我们来填一下上面的几个坑。
1. #{} 和 ${} 的区别
- #{} 表示一个占位符,通过 #{} 可以实现 PreparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型的转换,#{} 可以有效的防止 sql 注入。
- #{} 可以接受签单类型或者 pojo 属性值。如果 parameterType 传输单个简单类型的值, #{} 括号中可以是 value 或者其他名称。
- 表示拼接sql串,通过 {} 可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换。 可以接收简单类型值或者pojo属性值,如果parameterType传输单个简单类型值, {} 括号中只能是 value。
2. parameterType 和 resultType
- parameterType 指定输入参数类型, mybatis 通过 ognl 从输入对象中获取参数值拼接在 sql 中。
- resultType 指定输入结果类型, mybatis 将 sql 查询结果的一行记录数据映射为 resultType 指定的类型对象。
3. selectOne 和 selectList
- selectOne 查询一条记录,如果使用 selectOne 查询多条记录则会抛出异常。不信你可以试试。
- selectList 可以查询多条记录或者一条记录。
结尾
这篇文章还真有点多,不过大致算是讲清楚了 mybatis 入门的增删改查。由于我是菜鸟,所以我的能力有限,写出的文章难免会有错误的情况,如果有错误,还请各位不吝指正。
可能有的同学会说,为什么不先学 hibernate,在这里我说明一下,hibernate 虽然是一个经典的 orm (对象-关系映射)数据库框架,但是它他笨重了,不如 mybatis 轻量。再就是我们前面刚学的 sql 语句,而 hibernate 是不需要编写 sql 语句的,它完全是使用面向对象的思想去实现的,使用的是 HQL(Hibernate Query Language) 语句去执行的,这对刚刚学习完 sql 语句的我们,并没有什么好处,出于巩固手写 sql 语句的能力,我先选择了 mybatis ,并不是说我们不学习 hibernate,hibernate 需要留到后面再去学比较好。个人看法,欢迎交流。