初见Mybatis
☞ 博客导航,带你有序的阅读和学习!
- 概述
-
- JDBC VS Mybatis
- 使用JDBC操作数据库
- 使用Mybatis操作数据库
- SqlSession执行SQL
- 操作步骤
- 方式1
- 方式2
- 方式3
- 完整的Mybatis案例
- 完整的配置文件
- 配置文件解析
- properties属性文件
- UserMapper接口文件
- UserMapper.xml接口文件
- 单元测试
- 测试结果
- 总结
- 最后
概述
在技术进步的过程中,往往是因为新的技术比老的使用操作更方便,或是性能更优。而现在要接触的Mybatis无疑比传统的JDBC和hibernate等框架有更优的地方,才会有其存在的理由。下面我们通过JDBC与Mybatis的简单操作进行对比,来认识Mybatis的好。
简介
来自官网:
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
说一下:Mybatis官网对于英语不好的工程师来说很友好,因为它是为数不多的有中文文档的技术官网。
JDBC VS Mybatis
使用JDBC操作数据库
DBUtils:
/**
* 描述:
* 类【DBUtils】
*
* @author ouYangHao
* @create 2019-09-17 14:55
*/
public class DBUtils {
/*用户名*/
private static final String username = "root";
/*密码*/
private static final String password = "root";
/*驱动名*/
private static final String driverClassName = "com.mysql.jdbc.Driver";
/*数据库连接地址*/
private static final String dbUrl = "jdbc:mysql://120.79.167.xxx:3306/mybatis
?useUnicode=true
&characterEncoding=UTF-8
&allowMultiQueries=true
&autoReconnect=true
&useSSL=false";
public static List<User> findUsersByUsername(String name){
List<User> users = new ArrayList<>();
/*连接*/
Connection connection = null;
/*预编译statement*/
PreparedStatement statement = null;
/*结果集*/
ResultSet resultSet = null;
try {
/*加载数据库驱动*/
Class.forName(driverClassName);
/*获取数据库连接*/
connection = DriverManager.getConnection(dbUrl, username, password);
/*定义SQL*/
String sql = " select * from tb_user where username = ? ";
/*创建预编译处理的statement*/
statement = connection.prepareStatement(sql);
/*设置参数*/
statement.setString(1,name);
/*执行SQL*/
resultSet = statement.executeQuery();
/*遍历结果集,封装对象*/
while (resultSet.next()){
User user = new User();
user.setId(resultSet.getInt("id"));
user.setUserId(resultSet.getString("user_id"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
user.setEmail(resultSet.getString("email"));
user.setPhone(resultSet.getString("phone"));
user.setGender(resultSet.getInt("gender"));
user.setBirthday(resultSet.getTime("birthday"));
user.setStatus(resultSet.getInt("status"));
user.setCreateTime(resultSet.getTimestamp("create_time"));
user.setCreateUser(resultSet.getString("create_user"));
user.setModifyTime(resultSet.getTimestamp("modify_time"));
user.setModifyUser(resultSet.getString("modify_user"));
users.add(user);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
try {
if (connection != null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null){
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (resultSet != null){
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return users;
}
public static void main(String[] args) {
List<User> users = DBUtils.findUsersByUsername("admin");
System.out.println(users);
}
}
使用Mybatis操作数据库
Mapper接口的方法:
List<User> findUsersByUsername(String username);
Mapper.xml文件的SQL:
<sql id="Base_Column_List">
id, user_id, username, password, email, phone, gender, birthday, status, create_time,
create_user, modify_time, modify_user
</sql>
<!--查询-->
<select id="findUsersByUsername" resultType="user">
select
<include refid="Base_Column_List"/>
from tb_user
<where>
username = #{username}
</where>
</select>
以上代码除去Mybatis的配置文件外,上面仅仅几行就可以实现JDBC几十行代码可以实现的效果。
Mybatis的配置文件:
<?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>
<!--
通过源码我们可以分析读取优先级:
1、在 properties 内部自定义的属性值第一个被读取
2、然后读取 resource 路径表示文件中的属性,如果有它会覆盖已经读取的属性;
如果 resource 路径不存在,那么读取 url 表示路径文件中的属性,如果有它会覆盖第一步读取的属性值
3、最后读取 parameterType 传递的属性值,它会覆盖已读取的同名的属性
-->
<!--引入properties的属性文件-->
<properties resource="mybatis.properties">
<property name="username" value="root"/>
</properties>
<!--设置使用驼峰命名-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logPrefix" value="##Mybatis##"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<!-- mybatis自动扫描包中的po类,自动定义别名,别名是类名(首字母大写或小写都可以,一般用小写) -->
<typeAliases>
<package name="com.ooyhao.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>
<!--mapper xml文件-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
mybatis.properties:
jdbc.driver= com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://120.79.167.88:3306/mybatis
?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&autoReconnect=true&useSSL=false
jdbc.username = root
jdbc.password = root
测试:
public TestDemo{
SqlSession sqlSession = null;
@Before
public void init(){
String resource = "mybatis-configuration.xml";
InputStream inputStream = RoleTest.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = build.openSession(true);
}
@Test
public void testFindUsersByUsername() {
//使用Mapper文件形式
String username = "admin";
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findUsersByUsername(username);
System.out.println(userList);
sqlSession.close();
}
}
我们可以看一下测试类:我们通过SqlSessionFactoryBuilder的build方法,结合Mybatis的全局配置文件创建出SqlSessionFactory,再利用SqlSessionFactory通过openSession方法创建一个SqlSession。然后通过SqlSession来操作SQL。
看一下项目结构:
SqlSession执行SQL
操作步骤
- 需要在对应的mapper文件中添加相关的sql配置。
- 获得配置文件的路径并通过Resources的getResourceAsStream/AsReader方法获取流
- InputStream getResourceAsStream(String resource) 返回值为字节流
- Reader getResourceAsReader(String resource) 返回值为字符流
- 通过SqlSessionFactoryBuilder创建对象
- 使用SqlSessionFactoryBuilder对象的build(Stream/Reader)方法创建SqlSessionFactory对象。
- 通过SQLSessionFactory对象的openSession方法创建一个SqlSession对象
- 通过SQLSession对象的相关方法进行对数据库的crud操作。
- 增删改时需要提交事务。
上面已经进行了数据库操作,我们再回头看一下是不是这样的步骤:
@Test
public void testFindUsersByUsername() {
//1.mybatis的全局配置文件。
String resource = "mybatis-configuration.xml";
//2.获取文件并将其读取成流。
InputStream inputStream =
RoleTest.class.getClassLoader().getResourceAsStream(resource);
//3.通过SqlSessionFactoryBuilder创建对象。
//4.通过build创建SQLSessionFactory。
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
//5.创建一个SqlSession对象
sqlSession = build.openSession(true);
//使用Mapper接口形式
String username = "admin";
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//6.进行数据库操作
List<User> userList = mapper.findUsersByUsername(username);
System.out.println(userList);
//7.前面设置了自动提交,所以不同提交事务,这里关闭资源。
sqlSession.close();
}
我们通过SqlSession指定Sql的有下列几种方式:
方式1
使用SqlSession直接通过namespace.id的形式操作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.ooyhao.mybatis1.mybatis.mapper.UserMapper">
<!--查询-->
<select id="findUsersByUsername" resultType="user">
select
<include refid="Base_Column_List"/>
from tb_user
<where>
username = #{username}
</where>
</select>
</mapper>
测试方法:
@Test
public void testMybatisFindUsersByUsernameXML() {
/*使用XML形式文件*/
String username = "admin";
List<User> list = sqlSession.selectList
("com.ooyhao.mybatis1.mybatis.mapper.UserMapper.findUsersByUsername", username);
System.out.println(list);
sqlSession.close();
}
提示:com.ooyhao.mybatis1.mybatis.mapper.UserMapper是xml配置文件的namespace。findUsersByUsername是sql的id。
方式2
使用Mapper接口结合注解的方式来执行SQL:
@Select(" select * from tb_user where username = #{value} ")
List<User> findUsersByUsername(String username);
测试方法:
@Test
public void testFindUsersByUsername() {
//使用Mapper文件形式
String username = "admin";
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findUsersByUsername(username);
System.out.println(userList);
sqlSession.close();
}
提示:这种方式通过在接口方法上使用注解,来替代在xml文件中的SQL,简单sql可以使用注解。
方式3
使用Mapper接口和XML文件关联来查询SQL并执行:
Mapper接口:
List<User> findUsersByUsername(String username);
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.ooyhao.mybatis1.mybatis.mapper.UserMapper">
<!--查询-->
<select id="findUsersByUsername" resultType="user">
select
<include refid="Base_Column_List"/>
from tb_user
<where>
username = #{username}
</where>
</select>
</mapper>
单元测试方法:
@Test
public void testFindUsersByUsername() {
//使用Mapper文件形式
String username = "admin";
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findUsersByUsername(username);
System.out.println(userList);
sqlSession.close();
}
这种情况后面使用最多,但是有一些需要遵循的规则:
//遵循四个原则
1、Mapper.xml文件中的namespace与mapper接口的类路径相同。
2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
mapper动态代理
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。
完整的Mybatis案例
完整的配置文件
<?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>
<!--
通过源码我们可以分析读取优先级:
1、在 properties 内部自定义的属性值第一个被读取
2、然后读取 resource 路径表示文件中的属性,如果有它会覆盖已经读取的属性;
如果 resource 路径不存在,那么读取 url 表示路径文件中的属性,
如果有它会覆盖第一步读取的属性值
3、最后读取 parameterType 传递的属性值,它会覆盖已读取的同名的属性
-->
<!--引入properties的属性文件,数据库配置-->
<properties resource="mybatis.properties">
</properties>
<!--设置使用驼峰命名-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logPrefix" value="##Mybatis##"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<!-- mybatis自动扫描包中的po类,自动定义别名,别名是类名(首字母大写或小写都可以,一般用小写) -->
<typeAliases>
<package name="com.ooyhao.mybatis3.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>
<!--mapper xml文件-->
<mappers>
<!--<package name="mapper"/>-->
<mapper resource="mapper/UserMapper.xml"/>
<mapper resource="mapper/RoleMapper.xml"/>
<mapper resource="mapper/VehicleMapper.xml"/>
</mappers>
</configuration>
配置文件解析
【通过properties 的resource属性引入properties文件】
<!--引入properties的属性文件,数据库配置-->
<properties resource="mybatis.properties"></properties>
【延迟加载技术】
<settings>
<!--开启驼峰命名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--日志输出-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--关闭强制延迟加载技术-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
【包别名】
<!--设置别名-->
<!--
mybatis自动扫描包中的po类,自动定义别名,别名是类名(首字母大写或小写都可以,一般用小写)
-->
<typeAliases>
<package name="com.ooyhao.mybatis3.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>
【通过在mapper标签中引入mapper文件】
(一定要注意每新创建一个mapper文件需要在配置文件中配置)
<!--mapper xml文件-->
<mappers>
<!--<package name="mapper"/>-->
<mapper resource="mapper/UserMapper.xml"/>
<mapper resource="mapper/RoleMapper.xml"/>
<mapper resource="mapper/VehicleMapper.xml"/>
</mappers>
properties属性文件
jdbc.driver= com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://120.79.167.xxx:3306/mybatis
?useUnicode=true&characterEncoding=UTF-8
&allowMultiQueries=true&autoReconnect=true&useSSL=false
jdbc.username = root
jdbc.password = root
UserMapper接口文件
public interface UserMapper {
User findUserWithRolesByUserId(Integer id);
}
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.ooyhao.mybatis3.mapper.UserMapper">
<resultMap id="BaseResultWithRole" type="com.ooyhao.mybatis3.bean.User">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="user_id" jdbcType="VARCHAR" property="userId"/>
<result column="username" jdbcType="VARCHAR" property="username"/>
<result column="password" jdbcType="VARCHAR" property="password"/>
<result column="email" jdbcType="VARCHAR" property="email"/>
<result column="phone" jdbcType="VARCHAR" property="phone"/>
<result column="gender" jdbcType="INTEGER" property="gender"/>
<result column="birthday" jdbcType="DATE" property="birthday"/>
<result column="status" jdbcType="INTEGER" property="status"/>
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
<result column="create_user" jdbcType="VARCHAR" property="createUser"/>
<result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
<result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
<collection property="roles" ofType="role" column="id" select="selectRole" />
</resultMap>
<resultMap id="selectRole" type="role">
<id column="cid" jdbcType="INTEGER" property="id"/>
<result column="role_name" jdbcType="VARCHAR" property="roleName"/>
<result column="description" jdbcType="VARCHAR" property="description"/>
<result column="status" jdbcType="INTEGER" property="status"/>
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
<result column="create_user" jdbcType="VARCHAR" property="createUser"/>
<result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
<result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
</resultMap>
<select id="findUserWithRolesByUserId" resultMap="BaseResultWithRole">
select
a.id,a.user_id,a.username,a.password,a.email,a.phone,a.gender,
a.birthday,a.status,a.create_time,a.create_user,a.modify_time,a.create_user
from tb_user a
where a.id = #{id}
</select>
<select id="selectRole" resultType="role" >
select b.* from tb_user_role a
left join tb_role b on a.role_id = b.id
where a.user_id = #{id}
</select>
</mapper>
单元测试
public class UserTest {
SqlSession sqlSession = null;
@Before
public void init(){
String resource = "mybatis-configuration.xml";
InputStream inputStream =
UserTest.class.getClassLoader().getResourceAsStream(resource);
sqlSession = new
SqlSessionFactoryBuilder().build(inputStream).openSession(true);
}
@Test
public void findUserWithRolesByUserId(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findUserWithRolesByUserId(1);
System.out.println(JSONObject.toJSONString(user));
sqlSession.close();
}
}
测试结果
总结
这一节仅仅通过JDBC和Mybatis的操作比较,来引出Mybatis,初一看,可能会觉得Mybatis配置文件很繁琐,但是当我们书写多个SQL之后,就会觉得Mybatis仅仅是在第一次配置有些繁琐,后面书写SQL会很方便,而使用JDBC方式,每次写sql都会连带着5,60%的与业务无关的代码 ,而且每次都写的是一样的,比如获取Connection,获取Statement,关闭资源。
源码地址:
https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Mybatis