文章目录
前言
在使用JDBC输出记录集时,获取不同类型的字段值需要使用不同的方法,这是极其不方便的.mybatis是一个轻量级的ORM(Object Relational Mapping)对象关系映射框架,实现了Java对象和表之间的映射,使Java程序员可以使用对象编程思维来操作数据库,解决了记录输出问题。
一、MyBatis概述
1.1传统JDBC编程
试想一下没有接触过mybatis之前,我们是怎么与数据库进行交互的?
1、 加载数据库驱动
2、 创建并获取数据库链接
3、 创建 statement 对象
4、 拼写 sql 语句
5、 设置 sql 语句中的占位符的值
6、 执行 sql 语句并获取结果
7、 对 sql 执行结果进行解析处理
8、 释放资源
public ArrayList<Room> roomList(String floor,String build) throws SQLException {
ArrayList<Room> myList = new ArrayList<>();
Connection con = null;
Statement st = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver"); // 1.注册JDBC驱动程序 :这需要初始化驱动程序,这样就可以打开与数据库的通信信道。
String user = "jdbc:mysql://localhost:3306/dormitory_db?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
String userName1 = "root";
String password1 = "root";
con = DriverManager.getConnection(user, userName1, password1);
PreparedStatement pst1 = con.prepareStatement("SELECT id,number FROM t_room WHERE height=? and buildingid=?");
pst1.setObject(1,floor);
pst1.setObject(2,build);
ResultSet res = pst1.executeQuery();
while (res.next()) {
Room room=new Room();
room.setId(res.getInt("id"));
room.setNumber(res.getInt("number"));
myList.add(room);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
}
return myList;
}
试想一下这种做法有什么缺点?
1.数据库频繁释放和连接,造成资源浪费(使用数据库连接池解决)。
2.如果要改动sql语句就需要改动JAVA源代码,不易维护。
3.手动获取查询到的结果,重复操作次数高
1.2 mybatis的历史
原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了 Google Code,随着开发团队转投Google Code 旗下,iBatis3.x正式更名为MyBatis。
Mybatis中文官网: https://mybatis.org/mybatis-3/zh/getting-started.html
1.3 mybatis是什么?
Mybatis是一个基于java的持久层框架,封装了对底层JDBC API的调用细节,并且能自动地将简单的Java对象POJO(plain Old java Object 普通的Java对象)映射成数据库中的记录,自动完成对Java数据库编程中的一些重复性工作。
1.4如何使用?
使用简单的XML或注解来配置和映射原生信息,将接口和 Java 的POJO映射成数据库中的记录。
1.5Mybatis架构
二、 MyBatis环境搭建
2.1导入MyBatis jar包和数据库驱动包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
2.2创建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>
<!--设置全局配置-->
<properties resource="config.properties"></properties>
<settings>
<setting name="logImpl" value="LOG4J"/>
<!--java使用标准驼峰,数据库使用下划线连接(数据库不区分大小写)-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--setting name="lazyLoadingEnabled" value="true"/>-->
<!--指定哪些方法触发延迟查询 -->
<setting name="lazyLoadTriggerMethods" value=""/>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
<!--设置别名-->
<typeAliases>
<typeAlias type="com.ffyc.mybatispro.model.Admin" alias="Admin"></typeAlias>
<typeAlias type="com.ffyc.mybatispro.model.Employee" alias="Employee"></typeAlias>
<typeAlias type="com.ffyc.mybatispro.model.Dept" alias="Dept"></typeAlias>
<typeAlias type="com.ffyc.mybatispro.model.Student" alias="Student"></typeAlias>
</typeAliases>
<!--可以修改默认环境-->
<environments default="development">
<!--开发环境-->
<environment id="development">
<!--事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源 使用c3p0连接池-->
<dataSource type="com.ffyc.mybatispro.util.C3p0DataSourceFactory">
<property name="driverClass" value="${driverClass}"/>
<property name="jdbcUrl" value="${jdbcUrl}"/>
<property name="user" value="${user}"/>
<property name="password" value="${password}"/>
<property name="initialPoolSize" value="10"/>
<property name="maxIdleTime" value="30"/>
<property name="maxPoolSize" value="100"/>
<property name="minPoolSize" value="10"/>
<property name="maxStatements" value="200"/>
</dataSource>
</environment>
<!--工作环境-->
<!--<environment id="work">
<!–事务类型–>
<transactionManager type="JDBC"></transactionManager>
<!–数据源–>
<dataSource type="POOLED">
<property name="driver" value=""/>
<property name="url" value=""/>
<property name="username" value=""/>
<property name="password" value=""/>
</dataSource>
</environment>-->
</environments>
<!--添加映射文件-->
<mappers>
<mapper resource="mapper/AdminMapper.xml"></mapper>
<mapper resource="mapper/EmployeeMapper.xml"></mapper>
<mapper resource="mapper/DeptMapper.xml"></mapper>
<mapper resource="mapper/Student.xml"></mapper>
</mappers>
</configuration>
2.3创建映射接口(Mapper接口 主要用于定义有哪些操作)
Mapper 接口开发方法只需要程序员编写 Mapper 接口(相当于 Dao 接口), 由 Mybatis框架根据接口定义创建接口的动态代理对象.
public interface AdminMapper {
void saveAdmin(Admin admin);
Admin selectAdmin(int id);
Admin selectAdminByTwo(@Param("name") String name,@Param("sex") String sex);
Admin selectAdminByMap(HashMap<String,Object>map);
}
2.4创建和编写sql映射文件(Mapper.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.ffyc.mybatispro.mapper.AdminMapper">
<!-- #{}一般用来传数据,采用预编译的方法,编译好的sql语句再取值,可以防止sql注入
${}一般用来传列名,会传入参数字符串,如果想用$传数据可以用'${}'
useGeneratedKeys="true" 开关
keyColumn="id" 主键列
keyProperty="id" 接收主键的属性
-->
<!--当数据库的列名和对象的属性名不相同时,可以使用resultMap-->
<resultMap id="AdminResultMap" type="Admin">
<!--主键列-->
<id column="id" property="id"/>
<!--非主键列-->
<result property="name" column="ename"/>
</resultMap>
<insert id="saveAdmin" parameterType="Admin" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into admin(name,sex,phone)values(#{name},#{sex},#{phone})
</insert>
<!--简单的参数类型不需要设置parameterType-->
<select id="selectAdmin" resultMap="AdminResultMap">
SELECT
e.id,
e.name ename,
d.name dname,
a.name aname
FROM employee e LEFT JOIN dept d ON d.id=e.id
LEFT JOIN admin a ON a.id=e.id
WHERE e.id=#{id}
</select>
<select id="selectAdminByTwo" resultType="Admin">
select * from admin where name=#{name} and sex=#{sex}
</select>
<!--使用resultMap的输出映射,而非resultType-->
<select id="selectAdminByMap" resultMap="AdminResultMap">
select * from admin where id=#{id}
</select>
</mapper>
#{} 占位符,是经过预编译的,编译好 SQL 语句再取值,#方式能够防止 sql 注入
#{}:select * from t_user where uid=#{uid}
${} 拼接符,会传入参数字符串,取值以后再去编译 SQL 语句,$方式无法防止Sql注入
${}:select * from t_user where uid= '1'
MyBatis 排序时使用 order by 动态参数时需要注意,用$而不是#修改
注意:
1.Mapper.xml 文件中的 namespace 与 mapper 接口的类路径相同。
2.Mapper 接口方法名和 Mapper.xml 中定义的每个 statement 的 id 相同
3.Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的类型相同
4.Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型相同
2.5测试 MyBatis
读取核心配置文件
创建SqlSessionFactory
openSession()
创建SqlSession
getMapper(Class class);获得接口代理对象
commit();提交事务
close();关闭
获得接口代理对象
代理对象.接口中的方法
SqlSessionFactory 接口一旦创建,SqlSessionFactory 就会在整个应用过程中始终存在,所以没有理由去销毁和再创建它,一个应用运行中也不建议多次创建 SqlSessionFactory。如 果真的那样做,会显得很耗时,所以我们创建一个工具类,用来专门创建SqlSessionFactory,保证一个应用运行中只创建一次.且提供获得SqlSession接口类型对象的方法,方便我们操作
public class SqlSessionFactoryUtil {
public static SqlSessionFactory sqlSessionFactory;
static{
Reader reader=null;
try {
/*读取配置文件*/
reader = Resources.getResourceAsReader("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
/*SqlSessionFactory 一旦创建,在整个应用程序中都存在,不用销毁再创建,反复传教很耗时 */
sqlSessionFactory= new SqlSessionFactoryBuilder().build(reader);
}
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
/*用来获得SqlSession接口类型对象的方法*/
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
测试代码
@org.junit.Test
public void save(){
Admin admin=new Admin();
admin.setName("王小丽");
admin.setPhone("13157900521");
admin.setSex("女");
/*SqlSessionFactory 一旦创建,在整个应用程序中都存在,不用销毁再创建,反复传教很耗时 */
/* SqlSession 创建数据库会话,封装了对数据库操作的方法*/
SqlSession sqlSession= SqlSessionFactoryUtil.getSqlSessionFactory().openSession();
AdminMapper adminMapper= sqlSession.getMapper(AdminMapper.class);
adminMapper.saveAdmin(admin);
System.out.println(admin.getId()); //可以获得新增数据的主键
sqlSession.commit();
sqlSession.close();
}
在数据库中查看插入成功
注意:使用单元测试需要在maven中加入依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>provided</scope> <!--不会打包到war包-->
</dependency>
注意:Mybatis默认不自动提交事务,在进行增删改操作时,需要自己提交事务,否则数据不会变化到数据库中
三、参数传递和结果处理
3.1参数传递
1.简单参数传递
简单的参数形式不需要使用parameterType参数定义
接口中的方法
Admin selectAdmin(int id);
sql映射文件
<!--简单的参数类型不需要设置parameterType-->
<select id="selectAdmin" resultType="Admin">
SELECT
e.id,
e.name ename,
d.name dname,
a.name aname
FROM employee e LEFT JOIN dept d ON d.id=e.id
LEFT JOIN admin a ON a.id=e.id
WHERE e.id=#{id}
</select>
2.多个参数传递
多个参数使用@Param(“id”)绑定
接口中的方法
Admin selectAdminByTwo(@Param("name") String name,@Param("sex") String sex);
sql映射文件
<select id="selectAdminByTwo" resultType="Admin">
select * from admin where name=#{name} and sex=#{sex}
</select>
3.复杂参数传递
如果传入一个复杂的对象,就需要使用 parameterType 参数进行类型定义
接口中的方法
void saveAdmin(Admin admin);
sql映射文件
<insert id="saveAdmin" parameterType="Admin">
insert into admin(name,sex,phone)values(#{name},#{sex},#{phone})
</insert>
4.Map对象传递
在sql中使用表达式获取map的键即可
接口中的方法
Admin selectAdminByMap(HashMap<String,Object>map);
sql映射文件
<select id="selectAdminByMap" resultType="Admin">
select * from admin where id=#{id}
</select>
3.2结果处理
1.返回简单基本类型
接口中的方法
public int findAdminCount();
sql映射文件
<select id="findAdminCount" resultType="int">
select count(*) from userInfo
</select>
2.POJO对象输出映射
如果表中的类名与类中的属性名完全相同,mybatis会自动将查询结果封装 到POJO对象中.
接口中的方法
Admin selectAdminByTwo(@Param("name") String name,@Param("sex") String sex);
sql映射文件
<select id="selectAdminByTwo" resultType="Admin">
select * from admin where name=#{name} and sex=#{sex}
</select>
3.定义resultMap
接口中的方法
Admin selectAdmin(int id);
sql映射文件
<!--当数据库的列名和对象的属性名不相同时,可以使用resultMap-->
<resultMap id="AdminResultMap" type="Admin">
<!--主键列-->
<id column="id" property="id"/>
<!--非主键列-->
<result column="ename" property="name"/>
</resultMap>
<select id="selectAdmin" resultMap="AdminResultMap">
SELECT
e.id,
e.name ename,
d.name dname,
a.name aname
FROM employee e LEFT JOIN dept d ON d.id=e.id
LEFT JOIN admin a ON a.id=e.id
WHERE e.id=#{id}
</select>
注意:
(1). resutlMap 的 id 属性是 resutlMap 的唯一标识
(2). id 标签映射主键,result 标签映射非主键
(3). property 设置 POJO 的属性名称,column 映射查询结果的列名称
4.多表关联处理结果集
部门实体类包含以下几个成员变量
private int id;
private String name;
private List<Employee> employees=new ArrayList<Employee>();
private Admin admin;
由于部门中有一个Admin对象和多个employees对象组成的List集合,所以利用普通的结果集对象无法实现封装
解决方法:多表关联处理结果集,利用resultMap 元素中association , collection 元素
association 一对一结果映射
Collection 关联元素处理一对多关联。
接口中的方法
Dept selectDept(int id);
sql映射文件
<resultMap id="DeptResultMap" type="Dept">
<!--主键列-->
<id column="id" property="id"/>
<!--非主键列-->
<result property="name" column="dname"/>
<association property="admin" javaType="Admin">
<result property="name" column="aname"/>
</association>
<collection property="employees" javaType="list" ofType="Employee">
<result property="name" column="ename"></result>
</collection>
</resultMap>
<select id="selectDept" resultMap="DeptResultMap">
SELECT
d.id,
d.name dname,
a.name aname,
e.name ename
FROM dept d
LEFT JOIN admin a ON d.adminid=a.id
LEFT JOIN employee e ON e.deptid=d.id
WHERE d.id=#{id}
</select>
总结
本章主要介绍了Mybatis的基本概述,mybatis的环境搭建以及参数传递和结果处理。Mybatis对数据访问层进行了封装,使程序员不用进行以往重复性的操作。Mybatis在java框架部分难度不算太大,但是作为第一个学习的框架,Mybatis可以为后续框架的学习打下基础。