MyBatis
创建Maven工程
打包方式为
<packaging>jar</packaging>
<packaging>war</packaging>
加入MyBatis核心
<dependencies>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
创建mybatis-config.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>
<!--
MyBatis核心配置文件中,标签的顺序:(不按标签顺序写会报错)
properties?,settings?,typeAliases?,typeHandlers?,
objectFactory?,objectWrapperFactory?,reflectorFactory?,
plugins?,environments?,databaseIdProvider?,mappers?
-->
<!--引入properties文件,此后就可以在当前文件中使用${key}的方式访问value-->
<properties resource="druid.properties"/>
<!--设置类型别名-->
<typeAliases>
<!-- typeAlias标签:设置某类型的别名 type:设置需要设置别名的类型;
alias:别名的名称,如果不主动设置该名称,那么改类型默认别名为Massage,且不区分大小写
但是该种方式,表或者类很多的话就要设置很多-->
<!--<typeAlias type="bean.Massage" alias="msg"></typeAlias>-->
<!--所以可以以包为单位,将此包下的所有的类型都设置默认的(类型名不区分大小写)类型别名-->
<package name="bean"></package><!--bean包下的所有类都设置了默认别名-->
</typeAliases>
<!--设置连接数据库的环境-->
<!--
environments:配置数据库的环境
属性:
default:设置默认使用的环境的id
-->
<environments default="development">
<!--
environment:设置一个具体的链接数据库的环境
属性:
id:设置环境的唯一标识,不能重复
-->
<environment id="test">
<!--
transactionManager:设置事务管理器
属性:
type:设置事务管理的方式
type="JDBC/MANAGED"
JDBC:表示使用JDBC原生的事务管理模式
MANAGED:被管理,例如Spring
-->
<transactionManager type="JDBC"/>
<!--
dataSource:设置数据源
属性:
type:设置数据源的类型
type="POOLED/UNPOOLED/JNDI"
POOLED:表示使用数据库连接池
UNPOOLED:表示不使用数据库连接池
JNDI:表示使用上下文中的数据源
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/jdbctest?
serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="zmj.666.999"/>
</dataSource>
</environment>
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driverClass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--<mapper resource="mappers/MassageMapper.xml"/>-->
<!--
以包为单位引入映射文件
要求:
1.mapper接口所在的包要和映射文件的包一致
2.mapper接口要和映射文件的名字一致
也就是说一个接口对应一个映射文件,包名和类名要相同,/隔断来设置包路径
-->
<package name="mapper"/>
</mappers>
</configuration>
创建log4j.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS}
%m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
创建数据库链接工具类
/**
* Created by KingsLanding on 2022/7/26 18:25
*/
public class sqlSessionUtils {
public static SqlSession getSqlSession(){
SqlSession sqlSession = null;
try {
//获取核心配置文件的输入流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//获取sql的会话对象SqlSession,这是Mybatis提供的操作数据库的对象(非自动提交)
// SqlSession sqlSession = sqlSessionFactory.openSession();
//获取sql的会话对象SqlSession,这是Mybatis提供的操作数据库的对象(自动提交)
sqlSession = sqlSessionFactory.openSession(true);
} catch (IOException e) {
e.printStackTrace();
}
return sqlSession;
}
}
方法接口
/**
* Created by KingsLanding on 2022/7/26 15:46
*/
public interface MassageMapper {
/**
* 插入一条数据
* @return
*/
public int InsertMassage();
/**
* 更新一条数据
*/
public void UpdateMassage();
/**
* 删除一条数据
*/
public void DeleteMassageOne();
/**
* 查询一条数据
* @return
*/
public Massage QueryMassageOne();
/**
* 查询多条数据
* @return
*/
public List<Massage> QueryMassageMore();
}
创建对应的映射文件
<?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="mapper.MassageMapper">
<!--
mapper接口和映射文件要保证两个一致:
1.mapper接口的全类名要和映射文件的namespace一致
2.mapper接口中的方法的方法名要和映射文件中的sql的id保持一致
-->
<!--int InsertMassage();-->
<insert id="InsertMassage">
insert into massage values(null,'mybatisLog4j',20,'1818323123')
</insert>
<!--void UpdateMassage();-->
<update id="UpdateMassage">
update massage set age=22 where id=11;
</update>
<!--DeleteMassageOne()-->
<delete id="DeleteMassageOne">
delete from massage where id=11;
</delete>
<!--
查询功能需要额外设置一个属性
resultType:设置结果类型,即查询的数据要转换为的java类型
resultMap:自定义映射,处理多对一或一对多的映射关系
-->
<!--Massage QueryMassageOne();-->
<select id="QueryMassageOne" resultType="bean.Massage">
select id,name,age,phone from massage where id=2;
</select>
<!--List QueryMassageMore();-->
<select id="QueryMassageMore" resultType="Massage">
select * from massage;
</select>
</mapper>
测试
@Test
public void testUpdate(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
//获取MassageMapper的代理实现类对象(就是不用去写他的实现类了,由该对象代理去做)
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
//调用massageMapper接口中的方法,实现功能
mapper.UpdateMassage();
//关闭sqlSession对象
sqlSession.close();
}
MyBatis获取参数值的两种方式
MyBatis获取参数值的两种方式:#{}和${}
#{}的本质是占位符赋值,${}的本质是字符串拼接
- 若mapper接口的方法的参数为单个的字面量类型
此时可以通过#{}和{}要加单引号
<!--List<Massage> QueryMassage(String name);-->
<select id="QueryMassage" resultType="Massage">
select * from massage where name = #{name};
select * from massage where name = '${name}';
</select>
-
若mapper接口方法的参数为多个字面量类型
此时MyBatis会将参数放在map集合中,以两种方式存储数据 1、以
arg0,arg1……为key,以参宿为value 2、以param1,param2……为key,参数为value 由此可知只需通过#{}和{}单引号问题
<!--Massage QueryMassage01(String name,String age);-->
<select id="QueryMassage01" resultType="Massage">
select * from massage where name=#{arg0} and age=#{arg1};
</select>
- 若mapper接口方法的参数为实体类的参数 只需要通过#{}和{}单引号问题
<insert id="InsertMassage01">
insert into massage values(null,#{name},#{age},#{phone});
</insert>
- 可以在mapper接口方法的参数上设置
@Param注解 此时MyBatis会将这些参数放到map中,以两种方式运行存储 1、以@Param注解的value属性为key,以参数为值 2、以param1,param2...为key,以参数为值 只需要通过#{}和{}单引号问题
<!--Massage QueryMassageParam(@Param("nameParam") String name ,@Param("ageParam") String age);-->
<select id="QueryMassageParam" resultType="Massage">
select * from massage where name=#{nameParam} and age=#{ageParam};
</select>
- 自定义map中的key和对应的value
/**
* 自定义map的方式,查询一条数据;目的主要是在映射文件中可以自定义key来获取value
* @param map
* @return
*/
public Massage QueryMassageMap(Map<String,Object> map);
实现类
@Test
public void testQueryMassageMap(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("name","zmj");
map.put("age",24);
Massage massage = mapper.QueryMassageMap(map);
System.out.println(massage);
sqlSession.close();
}
映射文件
<!--
3、自定义map中的key和对应的value
由此可知只需通过#{}和${}访问该map集合的key,就可以获取到对应的值,同时注意${}单引号问题
-->
<!--Massage QueryMassageMap(Map<String,Object> map);-->
<select id="QueryMassageMap" resultType="Massage">
select * from massage where name=#{name} and age=#{age};
</select>
类型别名
- MyBatis中为Java中常见的类型设置了类型别名 Integer:Integer,int int:_int,_integer
<!--public int QueryMassageCount();-->
<select id="QueryMassageCount" resultType="int">
select count(*) from massage;
</select>
<!--Map<String,Object> QueryMassageHashMap();-->
<select id="QueryMassageHashMap" resultType="map">
select name,phone from massage where id=#{id};
</select>
- 字段名和属性名不一致的情况,如何处理映射关系 1.为查询的字段设置别名,和属性名保持一致 2.全局配置:当字段符合MySQL的要求使用_(下划线),而属性符合java要求的驼峰原则时 此时可以在MyBatis的核心配置文件设置一个全局配置,可以自动将下划线映射为驼峰 t_name -> tName ; t_password -> tPassword
<!--将下划线映射为驼峰原则;将字段名中的下划线自动转换为驼峰命名的属性名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
resultMap:设置自定义的映射关系
resultMap:设置自定义的映射关系id:唯一标识type:处理映射关系的实体类的类型 常用的标签:association:处理多对一的映射关系id:处理主键和实体类中属性的映射关系result:处理普通字段和实体类中属性的映射关系column:设置映射关系中的字段名,必须是sql查询出的某个字段property:设置映射关系中的属性的属性名,必须是实体类类型中的属性名
<resultMap id="TestResultMap" type="trap">
<id column="t_id" property="tId"></id>
<result column="t_name" property="tName"></result>
<result column="t_password" property="tPassword"></result>
<result column="t_age" property="tAge"></result>
</resultMap>
<!--QueryTestOne(@Param("tId") Integer tId);-->
<select id="QueryTestOne" resultMap="TestResultMap">
select * from trap where t_id=#{tId};
</select>
多对一
接口TrapMapper
/**
* Created by KingsLanding on 2022/7/28 17:04
*/
public interface TrapMapper {
List<Trap> getTrapAll(@Param("tId") Integer tId);
Trap QueryTestOne(@Param("tId") Integer tId);
/**
* 多表联查方法测试
* @param id
* @return
*/
Trap getTrapAndMassageByTrapId(@Param("id")Integer id);
/**
* 分布查询方法测试
* @param tId
* @return
*/
Trap getTrapAndDepartmentByStep(@Param("tId") Integer tId);
/**
* 一对多的查询方法测试
* @param tId
* @return
*/
/*
Trap{tId=1, tName='zmj', tPassword='123456', tAge=22, department=null,
deptList=[Department{dId=1, dName='zmj', dSector='计算机', tId=1}]}
*/
Trap getTrapAndDepartmentById(@Param("tId")Integer tId);
/**
* 一对多的分步查询方法测试
* @param tId
* @return
*
* Trap{tId=2, tName='zmj1', tPassword='123456', tAge=21, department=null,
* deptList=[Department{dId=2, dName='zmj1', dSector='软件', tId=2}]}
*/
Trap getTrapAndDepartmentListByStep(@Param("tId") Integer tId);
}
多表联查
association:处理多对一的映射关系(处理实体类类型的属性)
collection:处理一对多的映射关系(处理集合类型的属性)
property:设置需要处理映射关系的属性的属性名
javaType:设置要处理的属性类型
<!--
association:处理多对一的映射关系(处理实体类类型的属性)
collection:处理一对多的映射关系(处理集合类型的属性)
property:设置需要处理映射关系的属性的属性名
javaType:设置要处理的属性类型
-->
<resultMap id="TrapResultMapTest2" type="trap">
<id column="t_id" property="tId"></id>
<result column="t_name" property="tName"></result>
<result column="t_password" property="tPassword"></result>
<result column="t_age" property="tAge"></result>
<association property="department" javaType="Department">
<id column="d_id" property="dId"></id>
<result column="d_name" property="dName"></result>
<result column="d_sector" property="dSector"></result>
</association>
</resultMap>
<!--Trap getTrapAndMassageByTrapId(@Param("id")Integer id);-->
<select id="getTrapAndMassageByTrapId" resultMap="TrapResultMapTest2">
select trap.*,department.*
from trap
left join department
on trap.t_id=department.d_id
where trap.t_id=#{id};
</select>
分步查询
property:设置需要处理映射关系的属性的"属性名"
select:设置分布查询sql的唯一标识,也就是第二个表的方法名+全类名
column:将第一个表中查询出的某个字段数据作为分步查询的第二个表的sql条件,以此关联
fetchType:在开启了延迟加载的环境中,通过该属性设置当前分步查询是否使用延迟加载
fetchType="eager"(立即加载)fetchType="lazy"(延迟加载)
<!--
property:设置需要处理映射关系的属性的"属性名"
select:设置分布查询sql的唯一标识,也就是第二个表的方法名+全类名
column:将第一个表中查询出的某个字段数据作为分步查询的第二个表的sql条件,以此关联
fetchType:在开启了延迟加载的环境中,通过该属性设置当前分步查询是否使用延迟加载
fetchType="eager"(立即加载)fetchType="lazy"(延迟加载)
-->
<resultMap id="getTrapAndDepartmentByStepResultMap" type="Trap">
<id column="t_id" property="tId"></id>
<result column="t_name" property="tName"></result>
<result column="t_password" property="tPassword"></result>
<result column="t_age" property="tAge"></result>
<association property="department" fetchType="eager"
select="mapper.DepartmentMapper.getTrapAndDepartment"
column="t_id"></association>
</resultMap>
<!--Trap getTrapAndDepartmentByStep(@Param("tId") Integer tId);-->
<select id="getTrapAndDepartmentByStep" resultMap="getTrapAndDepartmentByStepResultMap">
select * from trap where t_id=#{tId};
</select>
关联表
<!--Department getTrapAndDepartment(@Param("dId") Integer dId);-->
<select id="getTrapAndDepartment" resultType="Department">
select * from department where d_id=#{dId};
</select>
延迟加载
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载, fetchType="lazy(延迟加载)|eager(立即加载)"
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
一对多
多表联查
collection:处理一对多的映射关系(处理集合类型的属性)
ofType:设置集合类型的属性中存储的数据的类型
<!--
collection:处理一对多的映射关系(处理集合类型的属性)
ofType:设置集合类型的属性中存储的数据的类型
-->
<resultMap id="TrapAndDepartmentResultMap" type="Trap">
<id column="t_id" property="tId"></id>
<result column="t_name" property="tName"></result>
<result column="t_password" property="tPassword"></result>
<result column="t_age" property="tAge"></result>
<collection property="deptList" ofType="Department">
<id column="d_Id" property="dId"></id>
<result column="d_name" property="dName"></result>
<result column="d_sector" property="dSector"></result>
<result column="t_id" property="tId"></result>
</collection>
</resultMap>
<!--Trap getTrapAndDepartmentById(@Param("tId")Integer tId);-->
<select id="getTrapAndDepartmentById" resultMap="TrapAndDepartmentResultMap">
select trap.*,department.*
from trap
left join department
on trap.t_id = department.t_id
where trap.t_id=#{tId};
</select>
分步查询
<resultMap id="TrapAndDepartmentListByStepResultMap" type="Trap">
<id column="t_id" property="tId"></id>
<result column="t_name" property="tName"></result>
<result column="t_password" property="tPassword"></result>
<result column="t_age" property="tAge"></result>
<collection property="deptList" fetchType="eager"
select="mapper.DepartmentMapper.getDepartmentListByTrapId"
column="t_id"></collection>
</resultMap>
<!--Trap getTrapAndDepartmentListByStep(@Param("tId") Integer tId);-->
<select id="getTrapAndDepartmentListByStep" resultMap="TrapAndDepartmentListByStepResultMap">
select * from trap where t_id=#{tId};
</select>
<!--List<Department> getDepartmentListByTrapId(@Param("tId") Integer tId);-->
<select id="getDepartmentListByTrapId" resultType="Department">
select * from department where t_id=#{tId};
</select>
特殊的sql操作
SpecialSQLMassageMapper
/**
* Created by KingsLanding on 2022/7/27 19:57
*/
public interface SpecialSQLMassageMapper {
/**
* 模糊查询
* @param str
* @return
*/
public List<Massage> FuzzyQueryMassage(@Param("str") String str);
/**
* 删除多条数据
* @param str
*/
public void DeleteMassageMore(@Param("str") String str);
/**
* 动态设置要查询的表名
* @param tableName
* @return
*/
@MapKey("id")
public Map QueryMassageFormTable(@Param("tableName") String tableName);
/**
* 添加一条用户信息,并获取用户自增的主键
* @param massage
*/
public void InsertMassageReturnKeyId(Massage massage);
}
SpecialSQLMassageMapper.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)保持一致-->
<mapper namespace="mapper.SpecialSQLMassageMapper">
<!--List<Massage> FuzzyQueryMassage(String str);-->
<select id="FuzzyQueryMassage" resultType="Massage">
<!--select * from massage where name like '%#{str}%' 这么写不行 因为是填充占位符的方式,也就是?无法解析-->
<!--select * from massage where name like '%${str}%';这个不常用,但是也可以,因为是拼接字符串的方式-->
<!--常用方法-->
select * from massage where name like "%"#{str}"%";
</select>
<!--void DeleteMassageMore(@Param("str") String str);-->
<delete id="DeleteMassageMore">
<!--#{}这种方式会自动加单引号'9,10'不能识别为两个id-->
delete from massage where id in(${str});
</delete>
<!--List<Massage> QueryMassageFormTable(@Param("tableName") String tableName);-->
<select id="QueryMassageFormTable" resultType="map">
select * from ${tableName};<!--这里也只能用${}-->
</select>
<!--
useGeneratedKeys:表示当前添加功能使用自增的主键
keyProperty:将添加的数据的自增主键为实体类类型的参数的属性赋值
-->
<!--void InsertMassageReturnKeyId(Massage massage);-->
<insert id="InsertMassageReturnKeyId" useGeneratedKeys="true" keyProperty="id">
insert into massage values (null ,#{name},#{age},#{phone});
</insert>
</mapper>
将查询的结果放到map中
接口方法
/**
* 查询的单条数据没有对应的实体类,那么就考虑将结果保存到map中
* @param id
* @return
*/
public Map<String,Object> QueryMassageHashMap(Integer id);
/**
* 查询的数据有多条,并且要将每条数据转换为map集合
* @return
*/
/*
1.查询的多数据没有对应的实体类,那么就考虑将结果保存到List<Map<String,Object>>中
也就是把多条map集合保存到list集合中
[{phone=122343235, name=zmj, id=1, age=24}, {phone=12138, name=zmj11, id=2, age=23}]
*/
public List<Map<String,Object>> QueryMassageAllListMap();
/*
2.可以讲每条数据转换的map集合放在一个大的map中,但是必须要通过@MapKey注解,将查询的某个字段的值
作为大map的key
1={phone=122343235, name=zmj, id=1, age=24}
2={phone=12138, name=zmj11, id=2, age=23}
*/
@MapKey("id")
public Map<String,Object> QueryMassageAllMap();
Mybatis映射文件
<!--Map<String,Object> QueryMassageHashMap();-->
<select id="QueryMassageHashMap" resultType="map">
select name,phone from massage where id=#{id};
</select>
<!--List<Map<String,Object>> QueryMassageAllListMap();-->
<select id="QueryMassageAllListMap" resultType="map">
select * from massage;
</select>
<!--Map<String,Object> QueryMassageAllMap();-->
<select id="QueryMassageAllMap" resultType="map">
select * from massage;
</select>
方法测试
@Test
public void testQueryMassageHashMap(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
Map<String, Object> stringObjectMap = mapper.QueryMassageHashMap(2);
//{phone=12138, name=zmj11}
System.out.println(stringObjectMap);
sqlSession.close();
}
@Test
public void testQueryMassageListMap(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
List<Map<String, Object>> list = mapper.QueryMassageAllListMap();
System.out.println(list);
}
@Test
public void testQueryMassageAllMap(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
Map<String, Object> map = mapper.QueryMassageAllMap();
Set<Map.Entry<String, Object>> entries = map.entrySet();
Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<String, Object> next = iterator.next();
System.out.println(next);
}
sqlSession.close();
}
SQL片段
<!--
sql片段:可以记录一段sql,在需要用的地方使用include标签进行引用
-->
<sql id="AllMassage">
id,name,age,phone
</sql>
<!--List<Massage> QueryAllMassage();-->
<select id="QueryAllMassage" resultType="com.spring.pojo.Massage">
select <include refid="AllMassage"></include> from massage;
</select>
动态SQL
1.if
通过test属性中的表达式判断标签中的内容是否有效,(是否会拼接到sql中)
2.where
1、若where标签中有条件成立,则自动生成where关键字 2、自动将where标签中内容前多余的and去掉,但是其中内容中的多余的and无法去掉 3、如果where标签中没有任何一个条件成立,则where没有任何功能
3.trim标签
1、prefix、suffix:在标签内容前面或后面添加指定的内容 2、prefixOverrides、suffixOverrides:在标签内容前面或后面去掉指定的内容
4.choose、when、otherwise
相当于java中的if...else if...else when至少设置一个,otherwise最多设置一个
<select id="QueryMassageByCondition" resultType="Massage">
select * from massage
<where>
<choose>
<when test="name !=null and name!=''">
name=#{name}
</when>
<when test="age !=null and age!=''">
and age=#{age}
</when>
<when test="phone != null and phone!=''">
and phone=#{phone}
</when>
</choose>
</where>
</select>
<select id="QueryMassageByConditionThere" resultType="Massage">
select * from massage
<trim prefix="where" suffixOverrides="and">
<if test="name !=null and name!=''">
name=#{name}
</if>
<if test="age !=null and age!=''">
and age=#{age}
</if>
<if test="phone != null and phone!=''">
and phone=#{phone}
</if>
</trim>
</select>
<select id="QueryMassageByConditionTwo" resultType="Massage">
select * from massage
<where>
<if test="name !=null and name!=''">
name=#{name}
</if>
<if test="age !=null and age!=''">
and age=#{age}
</if>
<if test="phone != null and phone!=''">
and phone=#{phone}
</if>
</where>
</select>
<select id="QueryMassageByConditionOne" resultType="Massage">
select * from massage where 1=1
<if test="name !=null and name!=''">
and name=#{name}
</if>
<if test="age !=null and age!=''">
and age=#{age}
</if>
<if test="phone != null and phone!=''">
and phone=#{phone}
</if>
</select>
5.foreach标签:循环
collection:设置要循环的数组或集合 item:用一个字符串表示数组或集合中的每一个数据 separator:设置每次循环的数据之间的分隔符 open:循环的所有内容以什么开始 close:循环的所有内容以什么结束
<!--void InsertMassageMore(@Param("massages")List<Massage> massages);-->
<insert id="InsertMassageMore">
insert into massage values
<foreach collection="massages" item="massage" separator=",">
(null,#{massage.name},#{massage.age},#{massage.phone})
</foreach>
</insert>
<!--void DeleteMassageMore(@Param("ids") Integer[] ids);-->
<delete id="DeleteMassageMore">
delete from massage where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
方法测试
@Test
public void testInsertMassageMore(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
Massage massage1 = new Massage(null, "foreach00", 33, "123123");
Massage massage2 = new Massage(null, "foreach01", 34, "123123");
Massage massage3 = new Massage(null, "foreach02", 35, "123123");
ArrayList<Massage> massageArrayList = new ArrayList<>();
massageArrayList.add(massage1);
massageArrayList.add(massage2);
massageArrayList.add(massage3);
mapper.InsertMassageMore(massageArrayList);
sqlSession.close();
}
@Test
public void testDeleteMassageMore(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
mapper.DeleteMassageMore(new Integer[]{17,18});
sqlSession.close();
}
Mybatis的缓存机制
相关依赖
<!-- Mybatis EHCache整合包 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
MyBatis的一级缓存:
MyBatis的一级缓存是`sqlSession`级别的,如果通过同一个`sqlSession`查询的数据都会被缓存
再次使用同一个sql查询同一条数据,则会从缓存中直接获取
1) 不同的`SqlSession`对应不同的一级缓存
2) 同一个`SqlSession`但是查询条件不同
3) 同一个`SqlSession`两次查询期间执行了任何一次增删改操作
4) 同一个`SqlSession`两次查询期间手动清空了缓存
@Test
public void testQueryAllMassage(){
long start = System.currentTimeMillis();
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
List<Massage> massages = mapper.QueryAllMassage();
for (Massage m:massages){
System.out.println(m);
}
long end = System.currentTimeMillis();
System.out.println("用时:"+(end-start));//用时:1432
System.out.println("**********************");
long start1 = System.currentTimeMillis();
MassageMapper mapper1 = sqlSession.getMapper(MassageMapper.class);
List<Massage> massages1 = mapper1.QueryAllMassage();
for (Massage m:massages1){
System.out.println(m);
}
long end1 = System.currentTimeMillis();
System.out.println("用时:"+(end1-start1));//1
}
MyBatis的二级缓存:
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被
缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
### 二级缓存开启的条件: a>在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置 b>在映射文件中设置标签 c>二级缓存必须在SqlSession关闭或提交之后有效 d>查询的数据所转换的实体类类型必须实现序列化的接口
public class Massage implements Serializable {
使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效(因为改了之后缓存里的数据就没有意义了) MyBatis缓存查询的顺序 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。 如果二级缓存没有命中,再查询一级缓存 如果一级缓存也没有命中,则查询数据库 SqlSession关闭之后,一级缓存中的数据会写入二级缓存
@Test
public void testCache() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
MassageMapper mapper1 = sqlSession1.getMapper(MassageMapper.class);
List<Massage> massages1 = mapper1.QueryAllMassage();
for (Massage m:massages1){
System.out.println(m);
}
sqlSession1.close();//不关闭,二级缓存失效
System.out.println("**************************");
MassageMapper mapper2 = sqlSession2.getMapper(MassageMapper.class);
List<Massage> massages2 = mapper2.QueryAllMassage();
for (Massage m:massages2){
System.out.println(m);
}
sqlSession1.close();//不关闭,二级缓存失效
}
Mybatis的逆向工程(豪华版)
相关依赖
<!-- 依赖MyBatis核心包 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。 Hibernate是支持正向工程的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
Java实体类
Mapper接口
Mapper映射文件
创建MyBatis的核心配置文件
创建逆向工程的配置文件 文件名必须是:generatorConfig.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>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/jdbctest?
serverTimezone=UTC"
userId="root"
password="zmj.666.999">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="pojo"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="massage" domainObjectName="Massage"/>
<table tableName="department" domainObjectName="Department"/>
</context>
</generatorConfiguration>
执行MBG插件的generate目标
方法测试
@Test
public void testMyBatis(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
//根据ID查询数据
Massage massage = mapper.selectByPrimaryKey(1);
System.out.println(massage);
System.out.println("*******************");
//根据条件查询
MassageExample massageExample = new MassageExample();
MassageExample.Criteria criteria = massageExample.createCriteria();
MassageExample.Criteria criteria1 = criteria.andAgeEqualTo(22);
System.out.println(criteria1);
List<Massage> massages = mapper.selectByExample(massageExample);
System.out.println(massages);
//测试普通修改功能,代表你提交的修改数据中存在null,但是也会执行,数据中会存一个null值
// mapper.updateByPrimaryKey(new Massage());
//测试选择性修改,无法提交null数据
// mapper.updateByPrimaryKeySelective(new Massage());
}
分页插件
添加依赖
<!--插件依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
配置MyBatis映射文件
<!--设置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
查询功能之前必须开启分页功能
//参数一:pageNum,当前页码;参数二:pageSize,显示的数据条数
Page<Object> page = PageHelper.startPage(2, 4);
方法测试
/**
* 分页插件功能测试
*/
@Test
public void testPage(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
MassageMapper mapper = sqlSession.getMapper(MassageMapper.class);
//查询功能之前必须开启分页功能
Page<Object> page = PageHelper.startPage(2, 4);
System.out.println(page);
List<Massage> massages = mapper.selectByExample(null);
for (Massage m:massages){
System.out.println(m);
}
//查询之后可以获取分页相关的所有数据
/*
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]
*/
PageInfo<Massage> massagePageInfo = new PageInfo<>(massages);
System.out.println(massagePageInfo);
/*
PageInfo{pageNum=2, pageSize=4, size=4, startRow=5, endRow=8, total=11, pages=3,
list=Page{count=true, pageNum=2, pageSize=4, startRow=4, endRow=8, total=11, pages=3, reasonable=false, pageSizeZero=false}
[Massage{id=7, name='zmj4', age=24, phone='18185352720'},
Massage{id=8, name='zmj5', age=23, phone='18185352720'},
Massage{id=9, name='zmj', age=21, phone='1234567'},
Massage{id=13, name='IdKey', age=22, phone='13333'}],
prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=8, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]}
*/
}
查询之后可以获取分页相关的所有数据
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]