一、总述:
单独使用mybatis框架对数据库进行增删改查整体思路如下:
导入相关jar包:
hamcrest-core-1.3.jar
junit-4.12.jar
log4j-1.2.17.jar
mybatis-3.5.6.jar
mysql-connector-java-5.1.37-bin.jar
在src下编写相应的类包以及基本配置文件{主要有mybatis配置文件,日志配置文件,数据库连接配置文件},主要有mapper包{主要用于存放mapper接口和对应的xml文件}、pojo(或entity)包{主要用于存放表格映射对象,即表格实体类}和test包{主要用于存放测试类}
二、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>
主要配置信息
</configuration>
加载数据库配置信息:在加载配置信息部分存在两种配置方式。
第一种配置方式:在配置文件中进行配置
<!--
properties用于配置键值对
resource属性指定加载的属性配置文件路径
-->
<properties>
<property name="url" value="jdbc:mysql://ip:端口/数据库"/>
<property name="username" value="账户"/>
<property name="password" value="密码"/>
<property name="driver" value="com.mysql.jdbc.Driver"/>
</properties>
第二中配置方式:引入外部配置文件
jdbc.properties配置文件{
url=jdbc:mysql://ip:端口/对应数据库?useSSL=false
username=root
password=
driver=com.mysql.jdbc.Driver
======================================================================
url=jdbc:mysql://ip:端口/对应数据库
username=root
password=root
driver=com.mysql.jdbc.Driver
}
===========================分割线======================================
在mybatis配置文件中引入jdbc.properties配置文件{
<!--
properties用于配置键值对
resource属性指定加载的属性配置文件路径
-->
<properties resource="jdbc.properties">
</properties>
}
配置延迟加载:延迟加载在一定程度上可以减少不必要的查询,给数据库服务器提升性能上的优化
<settings>
<!-- 打开延迟加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载 按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
定义多个别名:采用typeAliases标签定义多个别名
<typeAliases>
<!--
type属性是全类名
alias属性指定别名
<typeAlias type="com.lixxkv.mybatis.hello.pojo.UserOne" alias="userOne" />
-->
<!--
指定包名,mybatis会自动扫描这个包下所有类,自动映射别名
别名就是它的类名或类名首字母小写:
com.lixxkv.mybatis.hello.pojo.UserOne ====>>> User 或 userOne
-->
<package name="com/lixxkv/mybatis/hello/pojo"/>
<package name="com/lixxkv/mybatis/hello/domain"/>
</typeAliases>
配置连接数据库的信息
<!--
environments环境其实就是连接数据库的信息
default指定要连接哪一个数据库
-->
<environments default="development">
<!--
指定连接数据库的环境,其中用id标志使用的环境
id="development"开发环境
id="test"测试环境
environment是一个数据库连接的环境
id是唯一标识
-->
<environment id="development">
<!--
transactionManager标识事务管理器
type属性指定使用那个事务管理器
JDBC: 有事务管理,有提交和有回滚
MANAGED: 事务交给容器管理没有提交没有管理
-->
<transactionManager type="JDBC"/>
<!--
dataSource type="POOLED"表示使用数据库连接池
type属性有两个值:
UNPOOLED:不适用数据库连接池技术,名称使用connection连接对象需要手动创建,用完即关
POOLED:使用数据库连接池
-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<!--<environment id="test">-->
<!-- <transactionManager type="JDBC"/>-->
<!--dataSource type="POOLED"表示使用数据库连接池子-->
<!-- <dataSource type="POOLED">-->
<!-- <property name="driver" value="com.mysql.jdbc.Driver"/>-->
<!-- <property name="url" value="jdbc:mysql://ip:port/test?useSSL=false"/>-->
<!-- <property name="username" value="root"/>-->
<!-- <property name="password" value="root"/>-->
<!-- </dataSource>-->
<!--</environment>-->
</environments>
<databaseIdProvider type="DB_VENDOR">
<!--
name是数据库的名称
value是表示数据库的id值,这个id值可以写在databaseid属性中
-->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="DB2" value="db2"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
注册mapper文件信息:
<!--
mybatis是把sql语句放到配置文件中,而不写到java类中
设置Mapper映射文件(sql映射文件)
-->
<mappers>
<!-- 1、从类路径下加载mapper.xml配置文件-->
<!-- <mapper resource="com/lixxkv/mybatis/hello/mapper/UserMapperFour.xml"/>-->
<!--
2.表示按接口加载sql的配置文件
注意事项:
1.mapper接口和mapper.xml配置文件必须在同一个包下
2.mapper接口的类名必须和mapper.xml文件名称一致
-->
<!-- <mapper class="com.lixxkv.mybatis.hello.mapper.UserMapperOne"/>-->
<!-- 3.mybatis会自动根据指定的包名,加载所有的sql配置文件-->
<package name="com.lixxkv.mapper"/>
</mappers>
在已经注册mapper文件的情况下如果运行时仍然找不到对应的mapper文件而出现报错,那么要在pom.xml中添加以下代码:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
三、日志文件详解:
使用logforjava--log4j日志框架可以较为清楚地看见整个mybatis框架对后端查询的过程,因此可以将log4j日志框架引入项目中,首先引入log4j相关jar包,其次和数据库配置文件一样进行log4j配置文件(log4j.properties)的配置,其配置内容如下
#全局日志配置
log4j.rootLogger=DEBUG,stdout
#控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] -%m%n
四、对JavaBean的详解
在此部分,按照数据库表中字段,建立相应的属性,最后一张表就是一个JavaBean。在涉及一对一、多对多表关系时,按照关系在属性部分添加关系表的集合属性即可(如下)
//一对一情况下
//在班级表javabean中添加学生集合属性:
private List<Student> studentList;
//正常此属性的get/set方法
============================分割线===========================
//多对多情况下
//多对多的关系映射:一个用户可以具备多个角色,则在用户表中添加角色集合属性
private List<Role> roles;
//多对多关系映射,一个角色可以赋给过个用户。则在角色表中添加用户集合属性
private List<UserFive> userFives;
//剩下的get/set属性该怎么写就怎写
五、详解mybatis框架中具体实现对持久层进行操作的接口和接口对应的配置.xml文件
1.详解.xml文件
声明./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">
2.配置映射名称空间
<!--
mapper就是映射,映射mapper接口的方法和sql语句的对应关系
namespace(名称空间/命名空间)属性:设置为Mapper接口的全类名
-->
<mapper namespace="从src开始到指定的接口文件的全路径,其中以“.”做分隔符">
具体的查询方法声明及sql语句的定义
</mapper>
3.具体的查询方法声明及sql语句的定义
1)用于查询的声明
不带参数的查询
<select //标签表示配置select查询语句
id="对应mapper接口中对应方法名称"
parameterType="参数类型(可选设置,若是javaBean,则意见设置,是int类型即可省略)"
resultType="设置为方法的返回值的类型的全类名,指的是查询回来的每一行记录转换为具体的javabean类型"
databaseId="指定的sql是哪个厂场的,是mysql就只填写mysql(默认是mysql),是oracle就填oracle"
>
具体的sql查询语句,不带任何参数
select `id`,`username`,`birthday`,`sex`,`address` from user
</select>
==========================分割线===================================
查询结果是map的查询
mapper中{
@MapKey("id")//可以把返回结果转换为map对象,并指定一个属性作为key(唯一)
public Map<Integer, UserOne> queryUsersForMap();
}
mapper对应的xml中{
<!--
@MapKey("id"):可以把返回结果转换为map对象,并指定一个属性作为key(唯一)
public Map<Integer,User> queryUsersForMap();
-->
<select id="queryUsersForMap" resultType="userOne">
select `id`,`username`,`birthday`,`sex`,`address` from user
</select>
}
带普通参数的查询
javaBean对象属性与表中字段不一致查询{
在mappper接口中的方法:public List<User> queryUsers();
在对应的.xml中{
<!--
public List<User> queryUsers();
<resultMap>的作用(手动将数据库中字段名称与javaBean属性向对应起来):
它可以把查询的结果集映射成为需要的数据返回。
使用resultMap标签之前使用的是resultType属性,指定返回值的类型,此时mybatis的底层做的是自动映射
id属性表示一个唯一的标识
type属性指定和那个类做映射
一般来说,resultMap应用与复杂的javaBean对象上手动映射会取得较好效果,简单的javaBean对象属性均是普通类型
复杂的javaBean对象,其属性还会包含其他javaBean对象,或者集合类型中每一个元素又是另外一个javaBean对象,以上两种情况都叫复杂javaBean对象
-->
<resultMap id="resultMap1" type="全类名javabean">
<!--
id标签专门用于给主键列做映射关系
result标签用于非主键列做映射关系
column指定列名称
property指定属性名称
-->
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="字段1" property="属性1"/>
<result column="字段2" property="属性2"/>
</resultMap>
<select id="queryUsers" resultMap="resultMap1">
select 具体字段 from user
</select>
}
}
单个参数{
在mapper接口中的查询方法:public Xxx queryXxxById(Integer id);
对应.xml中的内容为{
//当接口参数类型为一个普通类型的时候则#{}中可以写任意内容,一般推荐写上参数名称,提高可读性
<select id="queryXxxById" resultType="src下全路径Xxx">
select 具体字段 from user where id=#{id}
</select>
}
}
多个参数{
在mapper接口中的查询方法:
public List<Xxx> queryXxxByNameOrId(String name,Integer id);
或
public List<Xxx> queryXxxByNameOrId(@Param("id") Integer id, @Param("username") String username);
对应.xml中的内容为{
/*
当参数位多个普通数据类型的时可以传递参数如下
param1 表示第一个参数
param2 表示第二个参数
...,以此类推
*/
<select id="queryUserByNameOrId" resultType="com.lixxkv.pojo.UserThree">
select 具体字段 from user where id=#{param1} or username=#{param2}
或
select 具体字段 from user where id=#{id} or username=#{username}
</select>
}
}
当map作为方法参数时{
/*
public List<User> queryUsersByMap(Map<String ,Object> paramMap);
若参数的类型是一个map只需要让map中的key和#{}占位符中的参数名称一致即可
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id",46);=====》》》》#{id}
paramMap.put("username","老王");======》》》》#{username}
for(User userOne : mapper.queryUsersByMap(paramMap)){
System.out.println(userOne);
}
*/
<select id="queryUsersByMap" resultType="com.lixxkv.pojo.User">
select 具体字段 from user where id=#{id} or username=#{username}
</select>
}
关于模糊查询{
/*
public List<User> queryUsersLikeName(String name);
Select * FROM userOne WHERE username LIKE li%
#{} 是占位符 ?
若只有一个指定参数,可以随意填写
${} 把指定参数输出。然后和sql中的内容做字符串拼接操作
可以写value
也可以写@Param注解来给参数命名使用
${}一般用于替换表
*/
在mapper接口中方法:public List<User> queryUsersLikeName(@Param("name") String name);
对应的.xml中{
<select id="queryUsersLikeName" resultType="com.lixxkv.pojo.UserTwo">
select 具体字段 from userOne where username like '${name}%'
</select>
<!--也可以使用concat函数实现模糊插查询-->
<select id="queryUsersLikeName" resultType="com.lixxkv.pojo.UserThree">
select 具体字段 from user where username like CONCAT(#{name},'%')
</select>
}
}
条件查询
在相应的mapper接口中的方法:public List<User> queryUserBySample(User user);
在相应的.xml中{
<!--
定义一个sql片段
id是唯一标识
-->
<sql id="sqlOne">
`id`,`username`,`birthday`,`sex`,`address`
</sql>
<!-- mapper中的方法接口实现通过用户的年龄或性别进行查询-->
<select id="queryUserBySample" parameterType="全路径.User" resultType="全路径.User">
select
<!--include是引入,refid是标识引入那个SQL判断-->
<include refid="sqlOne" />
from
user
<!--
trim可以动态地在包含的语句前面和后面进行指定内容的添加和删除
prefix 前面添加内容
prefixOverrides 删去前面内容
suffix 后面添加内容
suffixOverrides 删去后面内容
-->
<trim suffixOverrides="OR" prefix="WHERE">
<!--
if标签做if判断;if语句可以动态根据你的值决定是否想要动态修改查询条件,最终产生不同的sql语句
-->
<if test="username!=null">
username like concat(#{username},'%')
OR
</if>
age=#{age}
</trim>
</select>
==========================分割线======================================
<!--
在username,age中选择有效的进行执行,若都无效,则可以自定义查询条件
选择查询:choose(when,otherwise)语句:
类似switch case语句
-->
<select id="queryUserFourBySample" parameterType="全路径.User" resultType="全路径.User">
select
`id`,`username`,`birthday`,`sex`,`address`
from
user
<where>
<choose>
<when test="username!=null">
username like concat(#{username},'%')
</when>
<otherwise>
age=#{age}
OR
id=50
</otherwise>
</choose>
</where>
</select>
==========================分割线======================================
<!--
public List<UserFour> updateUserFouByIds(List<Integer> ids);
循环查询:
-->
<select id="updateUserFouByIds" resultType="com.lixxkv.pojo.UserFour">
select
<include refid="sqlOne" />
from
user
where
id in
<!--
foreach标签用于遍历操作
collection是遍历的数据源(参数名),默认参数名称:list
item当前遍历到的的数据
separator元素间的分隔符
open="(" 表示遍历开始的内容
close=")"表示变量结束的内容
-->
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
}
带缓存的查询
缓存:
把经常访问的数据保存到一个高速的缓冲区,方便后面访问的时候可以快速的获取到这些数据
缓存只有一个目标,就是为了提高访问速度
一级缓存:
同一个SqlSession对象
二级缓存:
同一个SqlSessionFactory对象
缓存的使用顺序说明
1.当mybatis执行一个查询的时候,会前去二级缓存中查询数据,有数据则直接返回
2.若二级缓存中无数据,再到一级缓存中取数据,有数据则直接返回
3.若一、二级缓存中无数据,则发送sql语句到数据库中查询
4.查到结果后会马上方到一级缓存中
5.当SqlSession关闭时,会把一级缓存中的数据同步到二级缓存中
mybatis在执行查询的时候会做以下的操作
1.mapper.queryUserById(50)会先从sqlSession缓存中查看是否有缓存数据
2.若sqlSession缓存中没有缓存数据,mapper.queryUserById(50)则发SQL语句到数据库进行查询
3.mapper.queryUserById(50)查完之后,将查询结果保存到sqlSession缓存中
一级缓存默认实现类:
org.apache.ibatis.cache.impl.PerpetualCache
一级缓存失效的四种情况:
1、不在同一个SqlSession对象中
2、执行语句的参数不同,缓存中也不存在数据
3、手动清空缓存数据---sqlSession.clearCache();//清空缓存
4、执行增删改语句会清空缓存
对应的mapper接口中的方法:public UserTwo queryUserById(Integer id);
自定义二级缓存{
/*
自定义二级缓存
1.编写一个实现Cache接口的实现类A
2.在mapper.xml配置文件的cache标签中,设置type="全类名.实现类A"
*/
public class MySecondCache implements Cache {
private final String id;
private Map<Object,Object> cache=new HashMap<>();
public MySecondCache(String id) {
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object key, Object value) {
System.out.println("自定义 二级缓存 保存数据 putObject");
this.cache.put(key,value);
}
@Override
public Object getObject(Object key) {
System.out.println("自定义 二级缓存 获取数据 getObject");
return this.cache.get(key);
}
@Override
public Object removeObject(Object key) {
return this.cache.remove(key);
}
@Override
public void clear() {
this.cache.clear();
}
@Override
public int getSize() {
return this.cache.size();
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MySecondCache that = (MySecondCache) o;
return Objects.equals(id, that.id) &&
Objects.equals(cache, that.cache);
}
@Override
public int hashCode() {
return Objects.hash(id, cache);
}
}
}
对应的.xml中{
<!--
使用二级缓存
type属性指定二级缓存实现类
-->
<cache type="全类路径.MySecondCache"/>
<!--
public User queryUserById(Integer id);
useCache="true"表示把查询结果放到二级缓存中
useCache="false"表示不把查询结果放到二级缓存中
-->
<select id="queryUserById" resultType="全路径.User" useCache="true">
select 具体字段 from user where id=#{id}
</select>
}
带表关系的查询
一对一查询
在持久层存在一对锁和钥匙表,其中锁表的主键是钥匙表的外键,表示一把钥匙配一把锁。在对象关系映射后的锁对象和钥匙类中,锁类仅仅拥有自己的属性,钥匙类除了拥有自己的属性外,还将锁类(不是锁对象的某一属性)引入了自己的属性中。
则对应的Keymapper.xml中主要的内容:
解决级联属性
<!--
id表示唯一标识,可以自定义
type表示和那个javaBean对象做列的映射关系
-->
<resultMap id="resultMapOne" type="全路径.Key">
<id column="id" property="id"/>
<result column="name" property="name"/>
<!--
级联属性映射
一级一级地去映射列
<result column="lock_id" property="lock.id"/>
<result column="lock_name" property="lock.name"/>
-->
<!--
使用association映射子对象的属性
property设置子对象(子对象属性名称)
-->
<association property="lock" javaType="全路径.Lock">
<result column="lock_id" property="id"/>
<result column="lock_name" property="name"/>
</association>
</resultMap>
通过钥匙id查询一对钥匙和锁
<!-- public Key queryKeyById(Integer id);-->
<select id="queryKeyById" resultMap="resultMapOne">
SELECT
t_key.id id,
t_key.`name` name,
t_lock.id lock_id,
t_lock.`name` lock_name
FROM
t_key LEFT JOIN t_lock
ON
t_lock.id=t_key.lock_id
WHERE
t_key.id=#{id}
</select>
仅仅对钥匙表进行查询
<resultMap id="resultMapTwo" type="全路径.Key">
<id column="id" property="id"/>
<result column="name" property="name"/>
<!--
association标签可以用于映射一个子对象
一种是通过调用一个查询来得到一个子对象
一种是通过建立查询好的列和子对象的属性来进行映射
property属性指定要映射那个子对象
select属性指定要使用那个查询得到子对象,在此处调用了对锁表操作的mapper接口中的方法queryLockById
column属性表示把结果集中的那个列作为参数给调用的方法的使用
-->
<association property="lock" column="lock_id" select="com.lixxkv.mapper.LockMapper.queryLockById" />
</resultMap>
<!--
public Key queryKeyByIdForSimple(Integer id);
-->
<select id="queryKeyByIdForSimple" resultMap="resultMapTwo">
SELECT `id`,`name`,`lock_id` FROM t_key WHERE id=#{id}
</select>
底层sql语句的实现
#内连接查询
SELECT
t_key.id key_id,
t_key.`name` key_name,
t_lock.id lock_id,
t_lock.`name` lock_name
FROM
t_key,t_lock
WHERE
t_lock.id=t_key.lock_id
AND
t_key.id=1
#外连接查询
SELECT
t_key.id key_id,
t_key.`name` key_name,
t_lock.id lock_id,
t_lock.`name` lock_name
FROM
t_key LEFT JOIN t_lock
ON
t_lock.id=t_key.lock_id
WHERE
t_key.id=2
一对多查询
在持久层存在学生班级表,其中班级表中的主键是学生表中的外键,表示一个班级可以有多个学生,一个学生只有一个班级。在对象关系映射后,学生类仅仅只有自己的属性,班级类除了拥有自己的属性外还将学生类引入自己的属性中。
则对应的学生mapper.xml
<!--
通过班级id查询学生信息
public List<Student> queryStudentByClazzId(Integer clazzId);
-->
<select id="queryStudentByClazzId" resultType="com.lixxkv.pojo.Student">
select `id`,`name` FROM t_student where clazz_id=#{classid}
</select>
<!--
通过班级id或班级模糊名字查询学生信息
public List<Student> queryStudentByClazzIdAndLikeName(
@Param("clazzId") Integer clazzId,
@Param("name") String name
);
-->
<select id="queryStudentByClazzIdAndLikeName" resultType="全路径.Student">
select `id`,`name` FROM t_student
where clazz_id=#{clazzId}
and name
like concat('%',#{name},'%')
</select>
对应的班级mapper.xml
解决级联问题
<resultMap id="resultMapOne" type="全路径.Clazz">
<id column="id" property="id"/>
<result column="name" property="name"/>
<!--
collection表示集合
property表示集合的属性名称
ofType表示集合元素类型
-->
<collection property="studentList" ofType="全路径.Student">
<id column="stu_id" property="id"/>
<result column="stu_name" property="name"/>
</collection>
</resultMap>
一次性,根据id查询出对应班级,以及班级所有学生信息
(public Clazz queryClazzById(Integer id);)
<select id="queryClazzById" resultMap="resultMapOne">
SELECT
t_clazz.*,
t_student.id stu_id,
t_student.`name` stu_name
FROM
t_student
LEFT JOIN
t_clazz
ON
t_clazz.id=t_student.clazz_id
WHERE
t_clazz.id=#{id}
</select>
根据班级编号查询,同时对学生姓名做模糊查询
public List<Student> queryStudentByClazzIdAndLikeName(
@Param("clazzId") Integer clazzId,
@Param("name") String name
);
<resultMap id="resultMapTwo" type="全路径.Clazz">
<id column="id" property="id"/>
<result column="name" property="name"/>
<!--
collection标签不仅可以把查询回来的结果映射成为一个集合
也可以调用一个查询,得到一个集合的结果
property是集合的属性名称!!!!!!!!
select属性指定调用那个查询(包名+类名+方法名称 或 名称空间+id)
column指定那个列作为调用方法的参数值
//根据班级编号查询,同时对学生姓名做模糊查询
public List<Student> queryStudentByClazzIdAndLikeName(
@Param("clazzId") Integer clazzId,
@Param("name") String name
)
以上方法需要两个参数,他们在sql语句中刚好是#{clazzId}和#{name},
多列赋值的格式如下:
{ clazzId=值,name=值 }
{ clazzId=id,name=name }
-->
<collection property="studentList" column="{ clazzId=id,name=name }"
select="全路径.StudentMapper.queryStudentByClazzIdAndLikeName"/>
</resultMap>
<!--
public Clazz queryClazzByIdFprTowStep(Integer id);
-->
<select id="queryClazzByIdFprTowStep" resultMap="resultMapTwo">
select `id`,`name` from t_clazz where id=#{id}
</select>
多对多查询
在持久层存在用户、角色表。一个用户对应多个角色,一个角色对应多个用户。在角色表中除了角色本身的属性以外,还将用户类关联到自己的属性中。同理在用户表中除了用户本身的属性以外,还将角色类关联到自己的属性中。
则对应的角色mapper
解决级联问题
<!--
定义roleMap
-->
<resultMap id="roleMap" type="全路径.Role">
<id property="roleId" column="rID"/>
<result property="roleName" column="ROLE_NAME"/>
<result property="roleDesc" column="ROLE_DESC"/>
<!-- property是javaBean中的属性集合名称-->
<collection property="userFives" ofType="全路径.UserFive">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
</collection>
</resultMap>
查询所有角色(public List<Role> findAll();)
<!--
查询所有
List<Role> findAll();
错误一:
resultType和resultMap混用
resultMap的id还不一致
-->
<select id="findAll" resultMap="roleMap">
select u.*,r.id as rid, r.ROLE_NAME,r.ROLE_DESC from role r
left outer join user_role ur
on r.id=ur.rid
left outer join `user` u
on u.id=ur.uid
</select>
对应的用户mapper
解决级联问题
<!--
定义roleMap
-->
<resultMap id="userMap" type="全路径.UserFive">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
<!-- property是javaBean中的属性集合名称-->
<collection property="roles" ofType="全路径.Role">
<id property="roleId" column="rID"/>
<result property="roleName" column="ROLE_NAME"/>
<result property="roleDesc" column="ROLE_DESC"/>
</collection>
</resultMap>
查询所有用户(public List<UserFive> findAllTwo();)
<!--
查询所有
public List<Role> findAllTwo();
-->
<select id="findAllTwo" resultMap="userMap">
select u.*,r.id as rid, r.ROLE_NAME,r.ROLE_DESC from `user` u
left outer join user_role ur
on u.id=ur.uid
left outer join role r
on r.id=ur.rid
</select>
2)用于插入
简单插入:
在mapper接口定义int类型插入方法,在对应的mapper.xml中使用insert标签在insert标签体内存在以下属性:
id="在mapper接口中定义的插入方法名称"
useGeneratedKeys="true"表示使用,即在日志中返回插入的id值
keyProperty="id"是指将在日志中返回的主键id值,注入到参数的那个属性中,
parameterType="全路径.插入对象类"
在inser标签中自定义插入语句:
insert into `表`(`字段列表`) values(#{字段一},#{字段二},...)
在insert标签中可以使用selectKey标签用于在日志中返回插入数据的主键值,在selectKey标签体中有以下属性
order属性设置两个语句的执行顺序(selectKey语句的执行顺序)
BEFORE:先执行selectKey语句
AFTER:后执行selectKey语句
resultType="int":表示返回Integer类型,即数据库中的影响的行数
keyProperty="id"是指将在日志中返回的主键id值,注入到参数的那个属性中
在selectKey中可以使用"select last_insert_id()"用于接收数据库中插入数据的主键值
当参数是一个javaBean对象(pojo对象),只需要再把#{}中写上属性名称即可
public class User {
private Integer id;
private String username; ====》》》#{username}
private Date birthday; ====》》》#{birthday} 以此类推
private String sex;
private String address;
}
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (null,"lixxkv","2022-03-04","男","上海")
插入实例
mapper接口定义方法
public int saveUser(User user);
对应的xml
方式一{
<insert id="saveUser" useGeneratedKeys="true" keyProperty="id"parameterType="全路径.User">
insert into `user`(`字段列表`) values(#{字段一},#{字段二},...)
</insert>
}
方式二{
<insert id="saveUser" parameterType="user">
<selectKey order="AFTER" keyProperty="id" resultType="int">
select last_insert_id()
</selectKey>
insert into `user`(`字段列表`) values(#{字段一},#{字段二},...)
</insert>
}
方式三{
<insert id="saveUser" parameterType="全路径.User">
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
}
带缓存的插入:
在update,delete,insert标签上,都有flushCache属性。表示是否清空缓存,默认是true,表示清空缓存
public int insertUser(User user);
flushCache="true"表示执行当前insert操作清空缓存,false则相反
实例一
mapper接口中的方法定义
public int insertUser(User user);
对应的mapper.xml中的定义
<insert id="insertUser" parameterType="全路径.User" flushCache="true">
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
多值插入:
可以在insert标签下使用foreach标签实现多只插入,在foreach标签体内有以下几个属性:
collection="list":声明方法中传入的参数是什么集合类型
item="UserFou":声明集合中每个个体是什么类
separator=",":将“,”做为集合中每个类的分割界限
实例一:
mapper接口中的方法定义
//inser into 表(列名,列名)value(值1,值2),(值1,值2),(值1,值2)
public int insertUser(List<User> users);
对应的mapper.xml
<insert id="insertUser" >
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`)
values
<foreach collection="list" item="User" separator=",">
(#{User.id},#{User.username},#{User.birthday},#{User.sex},#{User.address})
</foreach>
</insert>
3)用于更新
简单更新:
可以使用update标签,将对象作为参数对数据库中的表格数据进行更新,updat标签体的属性:parameterType="user"用于指定更新的数据对象。
实例
mapper接口中的自定义方法
public int updateUser(User user);
对应mapper.xml
<update id="updateUser" parameterType="user">
update user set `username`=#{username},`birthday`=#{birthday},`sex`=#{sex},`address`=#{address} where id=#{id}
</update>
指定更新:
使用set标签,可以对数据库表格中的数据可以进行指定更新,其中set标签可以删除set关键字条件前后的逗号
实例
mapper接口中自定义的方法
public int updateUser(User user);
对应的mapper.xm
<update id="updateUser" parameterType="全路径.UserFour" >
update
user
<set>
`username`=#{username},
`birthday`=#{birthday},
`sex`=#{sex},
<if test="address!=null">
`address`=#{address}
</if>
</set>
<where>
`id`=#{id}
</where>
</update>
4)删除
使用delete标签,其标签体内的属性:id="deleteUserById"用于指定mapper接口中绑定的方法
实例
mapper接口自定义方法
public int deleteUserById(Integer id);
对应mapper.xml
<delete id="deleteUserById">
delete from user where id=#{id}
</delete>
六、测试类详解篇
在搭建好mybatis框架后,对框架功能进行测试,在此处使用单元测试方法进行测试
在测试之前
定义SqlSessionFactory
static SqlSessionFactory sqlSessionFactory;
使用@Before注解(@Before注解标识的方法会在测试方法之前执行)对sqlSessionFactory进行初始化
@Before
public void init() throws IOException {
System.out.println("初始化");
//由mybatis-config.xml配置文件,先构建SqlSessionFactory对象实例
//读取mybatis-config.xml配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//可以简写
//sqlSessionFactory=new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
测试框架定义
@Test
public void functionName(){
//1通过SqlSessionFactory对象获取SqlSession;相当于jdbc中的Connection连接对象->用完即关。每定义一次SqlSession就有一个新的SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
//2通过SqlSession获取mapper接口(代理对象)
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//添加数据
User user = new User(null, "lixxkv", new Date(), "男", "上海");
//3.调用接口方法
int count = mapper.saveUser(user);
System.out.println("影响的行数=>"+count);
//手动提交事务
sqlSession.commit();
}catch (Exception e) {
e.printStackTrace();
}finally{
//4.关闭SqlSession对象
sqlSession.close();
}
}
七、注解开发
使用注解开发和不适用注解开发的区别仅仅只是在注解开发中少了mapper接口对应的.xml文件
在mapper接口中使用以下注解
@Select
@Insert
@Update
@Delete
@ResultMap//用于返回数据
查询实例
普通查询
@Select("select 字段列表 from user")
List<User> findAllAnn();
@Select("select 聚合函数 from user")
int findTotalUser();
带条件的查询
@Select("select 字段列表 from user where id=#{id}")
User findById(Integer userId);
//@Select("select 参数列表 from user where username like concat(#{username},'%')")
@Select("select 字段列表 from user where username like '${value}%'")
List<User> findUserByName(String username);
简单增,删和修改
@Insert("insert into `user`(`字段列表`) values (#{属性一},#{属性二},...")
void saveUser(User user);
@Update("update user set `username`=#{属性一},`birthday`=#{属性二},... where id=#{id}")
void updateUser(User user);
@Delete("delete from user where id=#{id}")
void deletUser(Integer userId);
在多对多条件下的特有查询,以用户(user)和账户(account)和为列,一个用户有多个账户,一个账户有一个用户
针对账户的操作
//由用户id查询账户信息
@Select("select 参数列表 from account where uid=#{userId}")
List<Account> findAccountByUid(Integer userId);
//查询所有账户,并且获取每个账户下所属用户信息
@Select("select 字段列表 from account")
@Results(id="accountMap",value={
@Result(id=true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
@Result(property = "user",column = "uid",one=@One(select="全路径.UserMapper.findById(方法名称)",fetchType= FetchType.EAGER))
})
List<Account> findAll();
针对用户的操作
//查询所有用户信息
@Select("select 字段列表 from user")
@Results(id="userMap", value={
@Result(id = true,column ="id",property = "userId"),
@Result(column ="username",property = "userName"),
@Result(column ="birthday",property = "userBirthday"),
@Result(column ="sex",property = "userSex"),
@Result(column ="address",property = "userAddress"),
@Result(property = "accounts",column = "id",many=@Many(select = "全路径.AccountMapper.findAccountByUid(方法名称)",fetchType = FetchType.LAZY))
})
List<UserSeven> findAllAnnSeven();
八、逆向工程
使用逆向工程时,在src同级目录下配置逆向工程配置.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>
相关配置信息
</generatorConfiguration>
设置版本
<!--
targetRuntime可以设置生成的版本
MyBatis3Simple:基本的增删改查
MyBatis3:带条件查询的增删改查
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
相关配置信息
</context>
去掉注释
<!--
去掉全部注释
-->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
修改数据库的四个连接属性
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://ip:端口/数据库"
userId="账户"
password="数据库代码">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
生成javaBean
<!--
javaModelGenerator生成javaBean
targetPackage指定生成的包
targetProject指定生成的项目位置
-->
<javaModelGenerator targetPackage="全路径.pojo" targetProject=".\mybatis-mbg\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
生成mapper.xml配置文件的信息
<!--
javaClientGenerator生成mapper接口
targetPackage指定生成的包
targetProject指定生成的项目位置
-->
<javaClientGenerator type="XMLMAPPER" targetPackage="全路径.mapper" targetProject=".\mybatis-mbg\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
逆向工程数据库选择
<!--
table标签,一个标签,一个数据库表
tableName表名
domainObjectName生成的类名
-->
<table tableName="user" domainObjectName="UserForMBG" ></table>
<table tableName="t_lock" domainObjectName="Tlock" ></table>
<table tableName="t_key" domainObjectName="Tkey" ></table>
在配置文件配置和后。设置启动类
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class Runner {
public static void main(String[] args) throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("mybatis-mbg/mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
运行启动类,运行成功后逆向工程完成