目录
一、什么是Mybatis
1.mybatis是什么?有什么特点?
它是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它的数据库无关性较低。简单来说,Mybatis是实现了Dao层访问数据库使用的一种框架,统一了不同数据库访问的规范
2.什么是ORM?
Object Relation Mapping,对象关系映射。对象指的是Java对象,关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应关系。
3.为什么mybatis是半自动的ORM框架?
用mybatis进行开发,需要手动编写SQL语句。而全自动的ORM框架,如hibernate,则不需要编写SQL语句。用hibernate开发,只需要定义好ORM映射关系,就可以直接进行CRUD操作了。由于mybatis需要手写SQL语句,所以它有较高的灵活性,可以根据需要,自由地对SQL进行定制,也因为要手写SQL,当要切换数据库时,SQL语句可能就要重写,所以mybatis的数据库无关性低。虽然mybatis需要手写SQL,但相比JDBC,它提供了输入映射和输出映射,可以很方便地进行SQL参数设置,以及结果集封装。并且还提供了关联查询和动态SQL等功能,极大地提升了开发的效率。并且它的学习成本也比hibernate低很多
二、使用步骤
1.导入依赖
<!--配置Mybatis驱动-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--配置sql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
Mybatis的使用主要涉及两个配置文件:主配置文件和Mapper映射文件;通过定义xml由java反射机制定位需要的类
2.主配置文件
参数说明:(简单说明常用的参数)
1. 引入外部文件
<properties resource="db.properties"></properties>
2. 开启日志
<setting name="logImpl" value="STDOUT_LOGGING"></setting>
3. 配置数据源
子标签<environment>可以配置多个数据源,用于不同的环境,通过id进行区分
<environments default="development">
<environment id="development">
<!--...-->
</environment>
</environments>
4. mapper映射文件
用于主配置文件和mapper文件之间产生映射关系,可以使用<mapper>或者<package>的方式定义;当使用<package>的时候,需要保证接口类和mapper.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"></properties>
<settings>
<!--设置日志-->
<setting name="logImpl" value="STDOUT_LOGGING"></setting>
</settings>
<typeAliases>
<!--将package包下的类作为别名-->
<package name="xxx"/>
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--分页参数合理化 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
<!--该部分应该由spring容器进行创建-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<!-- 使用相对于类路径的资源引用 -->
<!--sql映射文件位置-->
<mappers>
<mapper resource="xxx.xml"/>
<!--当使用包名的时候需要保证mapper映射文件和接口类在相同路径下-->
<!--<package name="xxx.dao"/>-->
</mappers>
</configuration>
3.Mapper映射文件
书写sql语句的xml文件,定义后通过主配置文件进行导入
1) Mapper.xml文件中的namespace与mapper接口的全限定名相同
2) Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3) Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
4) Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;还有一种返回类型是resultMap方式,可以自定义java对象属性和数据库列名的映射关系
基础模板:mapper映射文件可以通过逆工程技术自动实现,明白参数的含义
<?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="">
<select id="" resultType="">
</select>
</mapper>
三、实际应用
1. hello mybatis
配置文件模板,依赖在上面都提过,跳过了
测试用例:通过主键获取用户信息
UserDao为接口类,对外提供getUser(Integer id)方法
@Test
public void testMybatisConfig() {
String config = "mybatisConfig.xml";
try {
InputStream is = Resources.getResourceAsStream(config);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
// User user = sqlSession.selectOne("com.righteye.day01.dao.UserDao.getUser", 10);
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.getUser(10);
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
}
}
UserDao的映射文件:
<?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.righteye.day02.domain.User">
<!--接口方法名-->
<select id="getUser" resultType="com.righteye.day02.domain.User">
select * from user where id = #{id}
</select>
</mapper>
hello Mybatis主要实现使用Mybatis的步骤:
1.获取主配置文件:mybatisConfig.xml, 并进行加载
2.获取SqlSessionFactory对象
3.获取SqlSession对象(注:openSession中的参数如果为true,表示自动完成事务的提交,否则需要手动commit)
4.通过sqlSession获取接口类对象(反射机制自动完成)
mybatis的动态代理:mybatis根据dao的方法调用,获取执行sql语句的信息。根据接口创建一个接口的实现类,并创建这个类的对象完成sqlSession调用方法,访问数据库。
2.参数传递
1.基本类型传递
mybatis中的基本数据类型和string称为基本类型;使用#{任意字符} 进行传递
select * from user where id = #{id}
2. 多个参数的传递
传递多个参数的时候,在接口方法的形参位置前添加@Param, 为变量重命名,sql语句的书写时#{} 中的字符要和重命名相同
@Param 实现原理会将@Param修饰部分转换成一个Map对象,即方法中实际传入一个Map对象
Map<String,Object> map = new HashMap<>();
map.put("myname", username);
map.put("pwd",password);
接口方法声明:
int insertUser(@Param("myname")String username, @Param("pwd")String password);
UserMapper文件:
<?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.righteye.day02.domain.User">
<select id="insertUser" resultType="com.righteye.day02.domain.User">
select * from user where user_name = #{myname} and password = #{pwd}
</select>
</mapper>
3.使用java对象传递参数
使用java对象的属性值,作为传递参数的名字
4. 使用Map<String, Object>进行传递
接口方法, 在参数中传递Map:
User selectUser(Map<String,Object> map);
mapper映射文件
<select id="selectUser" parameterType="map" resultType="com.righteye.domain.User">
select * from user where name = #{username} and pwd = #{pwd}
</select>
Map<String, Object> map = new HashMap<String, Object>();
map.put("username","小明");
map.put("pwd","123456");
User user = mapper.selectUser(map);
3. # 和 $ 的区别
$ 底层使用jdbc的Statement对象,在sql语句中不存在?,而是使用字符串拼接;使用$可以替换sql中的字符串,用来替换表名、列名;
# 底层使用PreparedStatement,使用?进行占位,防止sql注入,保证数据安全;比$使用更加频繁
4.返回类型
1. resultType类型
Mybatis执行sql语句后的返回值;返回值是一个java对象,java对象的类型可以是简单数据类型或自定义类型
处理方式:mybatis执行sql语句,然后mybatis调用类的无参数构造方法创建对象,然后进行同名类赋值
<select id="getUser" resultType="com.righteye.day02.domain.User">
select * from user where id = #{id}
</select>
2. resultMap类型
resultMap:用于自定义属性和列名不统一;resultType默认数据库的值赋给同名java对象中的属性
<resultMap id="BaseResultMap" type="com.righteye.day02.domain.User">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="user_name" jdbcType="VARCHAR" property="userName" />
<result column="password" jdbcType="VARCHAR" property="password" />
</resultMap>
<select id="selectUserAndOrders" resultType="BaseResultMap">
select * from user u join orders o on u.id = o.uid where o.id = #{id};
</select>
5.动态sql
可以根据不同的条件获取不同的sql语句;主要是where部分
主要有三种标签:<if> <where> <foeach>
1. if 和 where
where标签就是代替了sql语句中的where条件;if就是分支判断
<select id="getTotalByCondition" resultType="int">
select count(*)
from tbl_activity a
join tbl_user u
on a.owner=u.id
<where>
<if test="name!=null and name!=''">
a.name like '%' #{name} '%'
</if>
<if test="owner!=null and owner!=''">
and u.name like '%' #{owner} '%'
</if>
</where>
</select>
<where>标签中如果有返回值的话则添加一个where, 如果标签返回值以and或者or开头则会进行剔除
2.foreach
语法:
<foreach>语句主要用于sql中的in语句,用于遍历集合
<foreach collection="" item="" begin="" end="" separator="">
collection:集合接口类型
item:自定义对象的名称
separator:间隔符
<delete id="delete">
delete from tbl_activity
where id in
<foreach collection="array" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
3.Sql片段
增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
提取sql片段
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
引用Sql片段
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="if-title-author"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</where>
</select>
注意:
①、最好基于 单表来定义 sql 片段,提高片段的可重用性
②、在 sql 片段中不要包括 where
上述代码来自:狂神说MyBatis06:动态SQL
6.逆向工程
mybatis官方提供mapper自动生成工具mybatis-generator-core来针对单表,生成实体类,以及Mapper接口和mapper.xml映射文件。针对单表,可以不需要再手动编写xml配置文件和mapper接口文件了,非常方便。但不支持生成关联查询
添加依赖:
<!--导入逆向工程依赖-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
</dependencies>
添加逆向工程插件
<build>
<!--导入逆向工程插件-->
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<!-- 配置文件的位置 -->
<configurationFile>generatorConfig.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</build>
注:这里的generatorConfig.xml文件是以项目为根目录的,不要放在资源路径下
完整genaratorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<classPathEntry>
<!--这个位置是maven仓库下mysql的jar包位置-->
location="xxx"/>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<!--表示生成的类是否需要注释-->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/xxx" userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 指定javaBean生成的位置 -->
<javaModelGenerator targetPackage="xxx"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--指定sql映射文件生成的位置 -->
<sqlMapGenerator targetPackage="xxx" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 指定dao接口生成的位置,mapper接口 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="xxx" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- table指定每个表的生成策略 -->
<table tableName="xx" domainObjectName="xx"></table>
</context>
</generatorConfiguration>
执行插件
7.多表联查
目标:给定user表和role表,属于一对多关系,目标找出每个用户对应的所有角色信息
表结构:
user表:
role表:
u_r表:表示user和role的关系表
首先给出普通的sql语句,在得出sql语句后再整合到mybatis中会更容易
select u.id, u.user_name, u.password, r.id rid, r.name from user u join u_r ur on u.id = ur.uid join role r on r.id = ur.rid
项目中使用逆向工程生成User类和Role类,为了存储查询的role信息,在User类在添加List<Role>属性,User类如下:
package com.righteye.day02.domain;
import java.util.List;
public class User {
private Integer id;
private String userName;
private String password;
private List<Role> rList;
public List<Role> getrList() {
return rList;
}
public void setrList(List<Role> rList) {
this.rList = rList;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName == null ? null : userName.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", rList=" + rList +
'}';
}
}
UserMapper接口类中方法声明:List<User> selectAllUserWithRole();
UserMapper.xml文件配置如下:文件书写顺序只表示我写的时候的流程
<!--
column用于指定用于关联查询的列
property用于指定要封装到StudentExt中的哪个属性
javaType用于指定关联查询得到的对象
select用于指定关联查询时,调用的是哪一个DQL
-->
<resultMap id="BaseResultMapWithRole" type="com.righteye.day02.domain.User">
<id column="id" property="id"></id>
<result column="user_name" property="userName"></result>
<result column="password" property="password"></result>
<!--返回的role是list集合,所以使用collection-->
<collection property="rList" ofType="com.righteye.day02.domain.Role">
<id column="rid" property="id"></id>
<result column="name" property="name"></result>
</collection>
</resultMap>
<sql id="Base_Column_List_withRole">
u.id, u.user_name, u.password, r.id rid, r.name
</sql>
<!--selectAllUserWithRole-->
<select id="selectAllUserWithRole" resultMap="BaseResultMapWithRole">
select
<include refid="Base_Column_List_withRole"></include>
from user u
join u_r ur on u.id = ur.uid
join role r on r.id = ur.rid
</select>
配置文件说明:
由于返回的是一个List<Role>对象,所以使用collection, ofType表示返回的对象类型
测试方法:
// 查询所有用户以及相关权限
@Test
public void test01() {
String config = "mybatisConfig.xml";
SqlSessionFactory sqlSessionFactory = null;
try {
InputStream is = Resources.getResourceAsStream(config);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
SqlSession session = sqlSessionFactory.openSession(true);
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> uList = userMapper.selectAllUserWithRole();
for (User user : uList) {
System.out.println(user);
}
}
运行结果:
8.多表联查 + 分页查询
在 7.多表联查的基础上进行更改,想要使用分页插件对查询的数据进行分页;分页代码:
@Test
public void test01() {
String config = "mybatisConfig.xml";
SqlSessionFactory sqlSessionFactory = null;
try {
InputStream is = Resources.getResourceAsStream(config);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
SqlSession session = sqlSessionFactory.openSession(true);
UserMapper userMapper = session.getMapper(UserMapper.class);
// 使用分页插件
PageHelper.startPage(2, 1);
List<User> uList = userMapper.selectAllUserWithRole();
PageInfo pageInfo = new PageInfo(uList, 5);
System.out.println(pageInfo);
}
查询结果如下:
可以发现分页查询后只出现了一个角色信息(上面最终测试结果有两个角色信息,分页失效了),原因在于sql语句书写问题,按 7.多表联查 的书写正常查询结果为:
分页的结果是在这个基础上进行的,所以为了将相同用户的所有角色全部查到需要修改sql语句,可以使用子查询,使用id一次性查询所有的role.name;修改UserMapper.xml配置文件:
<!--实现分页查询-->
<resultMap id="BaseResultMapWithRole" type="com.righteye.day02.domain.User">
<id column="id" property="id"></id>
<result column="user_name" property="userName"></result>
<result column="password" property="password"></result>
<!--返回的role是list集合,所以使用collection, 这里使用子查询-->
<collection property="rList" select="Base_Column_By_UserID" column="id">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
</collection>
</resultMap>
<sql id="Base_Column_List">
id, user_name, password
</sql>
<!--selectAllUserWithRole update-->
<select id="selectAllUserWithRole" resultMap="BaseResultMapWithRole">
select
<include refid="Base_Column_List"></include>
from user
</select>
<!--子查询-->
<select id="Base_Column_By_UserID" resultType="com.righteye.day02.domain.Role">
select id, name
from role join u_r ur on role.id = ur.rid
where ur.uid = #{id}
</select>
这里感觉类似group by u.id的思想,即将user按照id排序后依次查找每个id对应的所有角色,而前面那种方法是使用笛卡尔积得到所有的情况在进行筛选
9.主键返回
使用useGeneratedKeys
和keyProperty
属性
<insert id="insertEmp" useGeneratedKeys="true" keyColumn="u_id" keyProperty="uId">
insert into employee(u_name, u_birday, u_money, u_did, u_password)
values(#{uName}, #{uBirday}, #{uMoney},#{uDid}, #{uPassword})
</insert>
这种方法适用于数据库支持后去自增主键。keyProperty表示java实体类中的字段,keyColumn表示数据库表中的字段。
还有一种方法看下面学习连接
总结
简单记录目前掌握的Mybatis基础知识,待继续学习再做完善
学习链接:mybatis看这一篇就够了,简单全面一发入魂_v1009784814的博客-CSDN博客_mybatis看这一篇