MyBatis
一、快速起步
1、导入依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
2、创建数据库表
DROP table admin;
CREATE table admin(
id int PRIMARY KEY auto_increment,
name VARCHAR(32) not null,
password VARCHAR(32) not null,
age int,
telephone VARCHAR(11)
) ;
INSERT into admin(name,PASSWORD,age,telephone) values(‘wxb’,‘000000’,35,‘15191910792’);
INSERT into admin(name,PASSWORD,age,telephone) values(‘zsf’,‘000000’,34,‘15191764325’);
INSERT into admin(name,PASSWORD,age,telephone) values(‘wrf’,‘000000’,55,‘15674434377’);
3、在resouces下创建配置文件mybaits-config.xml
eviromenets表示mybatis的运行环境,这里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>
<!--eviromenets表示mybatis的运行环境,这里mybatis可以定义多个环境-->
<environments default="mysql">
<environment id="mysql">
<!--transactionManager表示事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<!--datasource表示数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF8"> </property>
<property name="username" value="root"></property>
<property name="password" value="Bigdata_2013"></property>
</dataSource>
</environment>
</environments>
</configuration>
4、编写实体类对象(根据数据库表创建实体类)
@Data
public class Admin {
private int id;
private String name;
private String password;
private int age;
private String telephone;
}
5、编写对应的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.wxb.mapper.AdminMapper">
<select id="selectCount" resultType="int" >
select count(*) from admin
</select>
</mapper>
6、在mybaits-config.xml的配置文件中引入上面的AdminMapper.xml文件
对于mybatis而言,一张数据库表对应一个映射文件,映射文件文件主要编写的sql语句
<mappers>
<mapper resource="mapper/AdminMapper.xml"></mapper>
</mappers>
将mapper.xml引入到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>
<!--eviromenets表示mybatis的运行环境,这里mybatis可以定义多个环境-->
<environments default="mysql">
<environment id="mysql">
<!--transactionManager表示事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<!--datasource表示数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF8"></property>
<property name="username" value="root"></property>
<property name="password" value="Bigdata_2013"></property>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/AdminMapper.xml"></mapper>
</mappers>
</configuration>
7、测试
@Test
public void mybatisHelloTest() {
InputStream resourceAsStream=null;
//根据mybatis-config.xml的配置文件,获得一个流对象InputSteam
try {
resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
//使用SqlSessionFactoryBuilder,用上面配置文件导出的流,获取一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//通过获取一个SqlSessionFactory来获取一个SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
Object obj = sqlSession.selectOne("com.wxb.mapper.AdminMapper.selectCount");//这里的参数是AdminMapper.xml中的namespace和id
if(obj instanceof Integer){
int count = (int)obj;
System.out.println("查询的个数是:"+count);
}
}
二、Mybait的工具类
public class MybaitsUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
InputStream resourceAsStream=null;
//根据mybatis-config.xml的配置文件,获得一个流对象InputSteam
try {
resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
//使用SqlSessionFactoryBuilder,用上面配置文件导出的流,获取一个SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
}
//给外界提供一个公共的方法用来获取SqlSession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
//给外界提供一个公共的关闭sqlSession的方法
public static void closeSqlSession(SqlSession sqlSession){
if(sqlSession!=null){
sqlSession.close();
}
}
}
三、mybatis基于接口和xml实现
原理图
1、创建接口
package com.wxb.mapper;
public interface AdminMapper {
public int getCount();
}
2、创建对应的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.wxb.mapper.AdminMapper">
<select id="getCount" resultType="int" >
select count(*) from admin
</select>
</mapper>
3、测试类
@Test
public void testOne()
{
SqlSession sqlSession = MybaitsUtil.getSqlSession();
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
int count = mapper.getCount();
System.out.println(count);
MybaitsUtil.closeSqlSession(sqlSession);
}
注意:在mapper.xml中的namespace和id要和接口类的全路径和方法名一一对应
四、<properties>引入外部资源文件
(如:数据库的连接配置文件)
1、在resources下创建datasource.properties文件,并加入数据库连接信息
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF8
username=root
password=Bigdata_2013
2、在mybati.config.xml中引入上面的配置文件,并使用el表达式进行赋值
在mybati.config.xml中引入datasource.properties的文件
<property name="driver" value="${driver}"></property>
并使用el表达式进行赋值
<configuration>
<!-- 在mybati.config.xml中引入datasource.properties的文件 -->
<properties resource="datasource.properties"></properties>
<!--eviromenets表示mybatis的运行环境,这里mybatis可以定义多个环境-->
<environments default="mysql">
<environment id="mysql">
<!--transactionManager表示事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<!--datasource表示数据源-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/AdminMapper.xml"></mapper>
</mappers>
</configuration>
五、日志操作log4j
1、导入依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、在resources下创建log4j的配置文件log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout,fileAppender
# Console output...设置控制台的输出信息
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#设置控制台的输出信息格式
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
log4j.appender.fileAppender = org.apache.log4j.FileAppender
log4j.appender.fileAppender.File = d:/log.log
log4j.appender.fileAppender.Append = true
log4j.appender.fileAppender.Threshold = DEBUG
log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
3、在mybatis的配置文件中通过<setting>标签导入log4j的文件(告知mybatis要使用log4j)
<!-- 使用log4j -->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4、使用
需要在使用时创建一个Logger的对象
public class AppTest {
//声明并实例化一个Logger对象
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void testLog4j()
{
SqlSession sqlSession = MybaitsUtil.getSqlSession();
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
int count = mapper.getCount();
logger.info("数量为:"+count);
MybaitsUtil.closeSqlSession(sqlSession);
}
}
5、推荐安装console grep插件,并且设置,设置如下
六、起别名 TypeAlias
1、在mapper.xml的文件中,resultType可以写简单名字Admin
<!--
resultType表示返回类型,这里一定要写类的全名(eg:com.wxb.pojo.Admin)
也可以写类别名(eg:Admin),但是一定要在配置文件中配置类别名 <typeAliases>
-->
<select id="findAdminById" parameterType="int" resultType="Admin">
select * from admin where id=#{id}
</select>
2、但是要在mybatis的配置文件mybatis-config.xml中,需要配置别名
<!-- 设置别名 -->
<!--第一种方法
<typeAliases>
<typeAlias type="com.wxb.pojo.Admin" alias="Admin"></typeAlias>
</typeAliases>
-->
<!--第二种方法
<typeAliases>
<typeAlias type="com.wxb.pojo.Admin" alias="Admin"></typeAlias>
</typeAliases>
-->
<typeAliases>
<package name="com.wxb.pojo"></package>
</typeAliases>
七、参数处理
1、单参数处理
a)接口
public interface AdminMapper {
public Admin findOneByName(String name);
}
b)mapper.xml
<!-- 单参数的处理 -->
<select id="findOneByName" resultType="admin" parameterType="String">
select * from admin where name=#{name}
</select>
c)测试
@Test
public void testOneParam() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
Admin admin = mapper.findOneByName("wxb");
logger.info(admin);
MybaitsUtil.closeSqlSession(sqlSession);
}
d)观察日志sql
#{key}这个key的内容,如果是单参数,MyBatis限制的很宽松,只要类型一样的就行了,不管名称取成什么都是可以的
2、多参数处理
2.1通过@Param方式来指定参数名称
<select id="findByNameAndAge" resultType="admin">
select * from admin where name like concat('%',#{name},'%') and age>#{age}
</select>
public interface AdminMapper {
public Admin findByNameAndAge(@Param("name") String name , @Param("age") int age);
}
如图:
2.2通过实体对象传值
public interface AdminMapper {
public Admin findByEntiy(Admin admin);
}
<select id="findByEntiy" parameterType="admin" resultType="admin">
select * from admin where name like concat('%',#{name},'%') and age>#{age}
</select>
通过实体对象来传值,实际上#{key} key的值是和实体对象中的属性名要保持一致的
2.3通过map传值
public interface AdminMapper {
public Admin findByMap(Map<String,Object> map);
}
<select id="findByMap" resultType="admin">
select * from admin where name like concat('%',#{name},'%') and age>#{age}
</select>
@Test
public void testManyParam3() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("name","s");
map.put("age",33);
Admin admin = mapper.findByMap(map);
logger.info(admin);
MybaitsUtil.closeSqlSession(sqlSession);
}
如果你使用的map作为参数的话,那么#{key} key的值是和map中的键的值是相同的,这样就能绑定到一起了
如上三种方式来处理参数的传值的问题,实际上底层都使用map集合
这三种在实际开发中都有用途
一般参数不超过三个的请使用第一种,那么如果参数超过3个以上可以使用实体或者使用map都可以
3、#{}和${}的区别
#{}这是就是JDBC中的预处理操作,PreparedStatement来完成预处理语句的执行操作,在预处理操作中使用?来占位,后续在赋值
${}这种方式JDBC中的拼接方式来完成
在使用${}完成查询的过程中,需要注意就是必须要在接口方法中参数前面带@Param(不管是一个参数还是多个参数)
在使用${}过程中会出现SQL注入,安全性低下,使用#{}不会出现SQL注入问题
两种方法都可以使用的情况下,建议使用#{}的方式
1)例如排序不支持占位就不能使用#{},只能使用${}
2) 例如在分表操作中就不能使用#{},只能使用${}
3) ${}也可以完成#{}相同的功能,进行参数传递,只不过它需要外面带单引号('')
注意:${}可能会引发SQL注入,#{}不会
八、返回结果
建表数据
CREATE TABLE `dept` (
`DEPTNO` int(2) NOT NULL,
`DNAME` varchar(14) DEFAULT NULL,
`LOC` varchar(13) DEFAULT NULL,
PRIMARY KEY (`DEPTNO`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `dept` VALUES ('10', 'ACCOUNTING', 'NEW YORK');
INSERT INTO `dept` VALUES ('20', 'RESEARCH', 'DALLAS');
INSERT INTO `dept` VALUES ('30', 'SALES', 'CHICAGO');
INSERT INTO `dept` VALUES ('40', 'OPERATIONS', 'BOSTON');
CREATE TABLE `emp` (
`EMPNO` int(4) NOT NULL,
`ENAME` varchar(10) DEFAULT NULL,
`JOB` varchar(9) DEFAULT NULL,
`MGR` int(4) DEFAULT NULL,
`HIREDATE` date DEFAULT NULL,
`SAL` int(7) DEFAULT NULL,
`COMM` int(7) DEFAULT NULL,
`DEPTNO` int(2) DEFAULT NULL,
PRIMARY KEY (`EMPNO`),
KEY `FK_DEPTNO` (`DEPTNO`),
CONSTRAINT `FK_DEPTNO` FOREIGN KEY (`DEPTNO`) REFERENCES `dept` (`DEPTNO`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `emp` VALUES ('7369', 'SMITH', 'CLERK', '7902', '1980-12-17', '800', null, '20');
INSERT INTO `emp` VALUES ('7499', 'ALLEN', 'SALESMAN', '7698', '1981-02-20', '1600', '300', '30');
INSERT INTO `emp` VALUES ('7521', 'WARD', 'SALESMAN', '7698', '1981-02-22', '1250', '500', '30');
INSERT INTO `emp` VALUES ('7566', 'JONES', 'MANAGER', '7839', '1981-04-02', '2975', null, '20');
INSERT INTO `emp` VALUES ('7654', 'MARTIN', 'SALESMAN', '7698', '1981-09-28', '1250', '1400', '30');
INSERT INTO `emp` VALUES ('7698', 'BLAKE', 'MANAGER', '7839', '1981-05-01', '2850', null, '30');
INSERT INTO `emp` VALUES ('7782', 'CLARK', 'MANAGER', '7839', '1981-06-09', '2450', null, '10');
INSERT INTO `emp` VALUES ('7788', 'SCOTT', 'ANALYST', '7566', '1987-04-19', '3000', null, '20');
INSERT INTO `emp` VALUES ('7839', 'KING', 'PRESIDENT', null, '1981-11-17', '5000', null, '10');
INSERT INTO `emp` VALUES ('7844', 'TURNER', 'SALESMAN','7698', '1981-09-08', '1500', '0', '30');
INSERT INTO `emp` VALUES ('7876', 'ADAMS', 'CLERK', '7788', '1987-05-23', '1100', null, '20');
INSERT INTO `emp` VALUES ('7900', 'JAMES', 'CLERK', '7698', '1981-12-03', '950', null, '30');
INSERT INTO `emp` VALUES ('7902', 'FORD', 'ANALYST', '7566', '1981-12-03', '3000', null, '20');
INSERT INTO `emp` VALUES ('7934', 'MILLER', 'CLERK', '7782', '1982-01-23', '1300', null, '10');
1、返回单个对象
public Emp selectEmpByEname(@Param("ename")String ename)
<select id="selectEmpByEname" resultType="emp" parameterType="String">
SELECT * FROM emp WHERE ename=#{ename}
</select>
2、返回List集合
public List<Emp> selectAllByDept(@Param("tableName") String tableName);
<select id="selectAllByDept" resultType="emp" parameterType="String">
SELECT * FROM emp_${tableName}
</select>
注意:如果方法的返回值类型是List类型,那么在映射配置文件中节点中的resultType的属性的值是这个List集合中元素的类型
3、返回类型是Map
3.1) 返回map类型的单个对象
接口
public Map<String,Object> selectEmpByEmpno(short empno);
映射文件中的配置
<select id="selectEmpByEmpno" parameterType="short" resultType="map">
SELECT * FROM emp where empno=#{empno}
</select>
编写测试方法
@Test
public void test4(){
SqlSession sqlSession= MyBatisUtil.crateSqlSession();
EmpMapper empMapper=sqlSession.getMapper(EmpMapper.class);
Map<String,Object> map=empMapper.selectEmpByEmpno((short)7369);
logger.info(map);
MyBatisUtil.closeSqlSession(sqlSession);
}
3.2 )返回多条记录以Map作为返回值
接口
@MapKey("empno")
public Map<String,Emp> selectEmpsByLikeEnameReturnMap(String ename);
在接口中通过@Mapkey来指定Map集合的键
映射文件
<select id="selectEmpsByLikeEnameReturnMap" parameterType="string" resultType="emp">
SELECT * FROM emp WHERE ename LIKE CONCAT('%',#{ename},'%')
</select>
映射文件中resultType的值是Map集合中value的类型
测试
@Test
public void test5(){
SqlSession sqlSession= MyBatisUtil.crateSqlSession();
EmpMapper empMapper=sqlSession.getMapper(EmpMapper.class);
Map<String,Emp> map=empMapper.selectEmpsByLikeEnameReturnMap("A");
logger.info(map);
MyBatisUtil.closeSqlSession(sqlSession);
}
4、数据库列名和实体对象属性不一致的解决方案
之前数据库表字段和对象的属性名相同,在映射文件中通过resultType就能实现自动的对应,如果数据库的字段和实体对象不一致
A)使用AS关键字来解决
<select id="findAllPerson" resultType="person">
SELECT person_id AS personId,person_name AS personName,person_age personAge
FROM person
</select
B)如果你的项目中采用驼峰命名法,那么可以在mybatis-cofig.xml文件中进行设置
// 例如:person_id==========>personId
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"></setting>
</settings>
注意:如上设置一定要符合驼峰命名法才行,否则不能起到效果
C)采用resultMap方式来解决(推荐使用)
c.1准备数据表
CREATE TABLE person(
person_id INT PRIMARY KEY AUTO_INCREMENT,
person_name VARCHAR(32),
person_age INT
);
INSERT INTO person(person_name,person_age)VALUES('张三',23);
INSERT INTO person(person_name,person_age)VALUES('李四',25);
c.2实体对象
@Data
public class Person {
private Integer personId;
private String personName;
private String personAge;
}
c.3接口方法
public interface PersonMapper {
public List<Person> findAllPerson();
}
c.4映射文件
<mapper namespace="com.wxb.mapper.PersonMapper">
<resultMap id="personMap" type="person">
<!--
id作为数据库的主键声明的,常用属性
property:实体对象的属性名
column:数据库字段名
result:非主键字段的声明
-->
<id property="personId" column="person_id"></id>
<result property="personName" column="person_name"></result>
<result property="personAge" column="person_age"></result>
</resultMap>
<select id="findAllPerson" resultMap="personMap">
select * from person
</select>
</mapper>
c.5测试
@Test
public void testResultMap() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
List<Person> allPerson = mapper.findAllPerson();
for(Person p:allPerson){
logger.info(p);
}
MybaitsUtil.closeSqlSession(sqlSession);
}
九、关联关系(一对一)
A) association嵌套映射
1、建表代码
#身份证表
CREATE TABLE tb_card(
cid INT PRIMARY KEY AUTO_INCREMENT,
CODE VARCHAR(18)
);
#测试数据
INSERT INTO tb_card(CODE)VALUES('610122198003084020');
#公民表
CREATE TABLE tb_person(
pid INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(50),
age INT,
sex VARCHAR(20),
idcard INT UNIQUE,
FOREIGN KEY(idcard) REFERENCES tb_card(cid)
);
#测试数据
INSERT INTO tb_person(NAME,age,sex,idcard)VALUES('张三',32,'男',1);
2、创建实体对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private int pid;
private String name;
private int age;
private String sex;
private Card card;
}
3、编写mapper.xml文件
<mapper namespace="com.wxb.mapper.PersonMapper">
<resultMap id="personMap" type="person">
<id property="pid" column="pid"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<!--
association:用于处理一的一方设置,常用属性
property:关联对象的属性名
javaType:关联对象的类型(类全名,别名(必须要在配置文件中配置typeAliases))
-->
<association javaType="card" property="card">
<id property="cid" column="cid"></id>
<result property="code" column="code"></result>
</association>
</resultMap>
<select id="findAll" resultMap="personMap">
SELECT p.*,c.*
FROM tb_person p INNER JOIN tb_card c on p.idcard=c.cid
</select>
</mapper>
4、测试代码
public class AppTest
{
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void testOne2One() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
List<Person> all = mapper.findAll();
for(Person p : all){
logger.info(p.getName()+"\t"+p.getSex()+"\t"+p.getCard().getCode());
}
MybaitsUtil.closeSqlSession(sqlSession);
}
}
B) association分步查询【重点掌握】
1、创建实体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private int pid;
private String name;
private int age;
private String sex;
private Card card;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Card {
private int cid;
private String code;
}
2、编写接口
public interface PersonMapper {
public List<Person> findAll();
}
public interface CardMapper {
public Card findById(@Param("cid") int cid);
}
3、编写mapper.xml文件
PersonMapper.xml
<mapper namespace="com.wxb.mapper.PersonMapper">
<resultMap id="personMap" type="person">
<id property="pid" column="pid"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<!--
SELECT * FROM tb_person;
SELECT * FROM tb_card WHERE cid=1
association:一对一关联
property:关联对象属性名
select:分布查询的方法名
column:外键
-->
<association property="card" select="com.wxb.mapper.CardMapper.findById" column="idcard">
</association>
</resultMap>
<select id="findAll" resultMap="personMap">
select * from tb_person
</select>
</mapper>
CardMapper.xml
<mapper namespace="com.wxb.mapper.CardMapper">
<select id="findById" parameterType="int" resultType="card">
select * from tb_card where cid=#{cid}
</select>
</mapper>
4、在mybatis-config.xml主配置文件中引入两个子mapper.xml文件
<mappers>
<mapper resource="mapper/PersonMapper.xml"></mapper>
<mapper resource="mapper/CardMapper.xml"></mapper>
</mappers>
C)association分步查询的延迟加载问题【掌握】
1、第一种方式(建议使用)
通过在mybatis-config.xml中配置
<settings>
<!--
lazyLoadingEnabled是开启全局的懒加载开关
-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--
aggressiveLazyLoading是做什么么的?
它是控制具有懒加载特性的对象的属性的加载情况的。
true表示如果对具有懒加载特性的对象的任意调用会导致这个对象的完整加载,
false表示每种属性按照需要加载。
-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
2、第二种方式
通过在association中设置fetchType的值来设置延迟加载
<association property="card"
select="com.wxb.mapper.CardMapper.findById"
column="idcard" fetchType="lazy">
</association>
3、配置前后sql对比
使用之前
使用之后
4、为什么建议第一种方式
如果在association中通过fetchType设置延迟加载和通过在mybatis-config.xml文件通过setting节点设置延迟加载同时存在时,fetchType设置延迟加载的级别要高于setting中设置的级别
比如:100条sql,需要极个别的几个不使用延迟加载,只用在对应的association中设置fetchType的值为eager即可
十、关联关系(一对多)
A)collection嵌套映射的配置方式(单向一对多)
1、创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private int empno;
private String ename;
private String job;
private int mgr;
private Date hiredata;
private int sal;
private int comm;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
private int deptno;
private String dname;
private String loc;
private List<Emp> emps;
}
2、创建mapper.xml
<mapper namespace="com.wxb.mapper.DeptMapper">
<resultMap id="deptMap" type="dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<!--
collection:在一方配置多方关联的节点,常用的属性如下
|- property: 关联对象的属性名
|-ofType:表示的是集合中的元素的类型,可以是类全名,也可以是别名
-->
<collection property="emps" ofType="emp">
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="mgr" column="mgr"></result>
<result property="hiredata" column="hiredata"></result>
<result property="sal" column="sal"></result>
<result property="comm" column="comm"></result>
</collection>
</resultMap>
<select id="findByDeptno" parameterType="int" resultMap="deptMap">
SELECT d.*,e.*
FROM dept d INNER JOIN emp e on d.deptno=e.deptno
WHERE d.deptno=#{deptno}
</select>
</mapper>
3、编写接口
public interface DeptMapper {
public Dept findByDeptno(@Param("deptno") int deptno);
}
4、测试
public class AppTest
{
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void test1() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.findByDeptno(10);
logger.info(dept.getDname()+"\t"+dept.getLoc());
List<Emp> emps = dept.getEmps();
for(Emp e : emps){
logger.info(e.getEname()+"\t"+e.getHiredata()+"\t"+e.getJob());
}
MybaitsUtil.closeSqlSession(sqlSession);
}
}
B)collection分步查询的配置一对多的方式(单向一对多)
1、创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private int empno;
private String ename;
private String job;
private int mgr;
private Date hiredata;
private int sal;
private int comm;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
private int deptno;
private String dname;
private String loc;
private List<Emp> emps;
}
2、创建mapper.xml
<mapper namespace="com.wxb.mapper.DeptMapper">
<resultMap id="deptMap" type="dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<!--
collection:在一方配置多方关联的节点,常用的属性如下
|- property: 关联对象的属性名
|-column:表示的是外键
-->
<collection property="emps"
select="com.wxb.mapper.EmpMapper.findEmpsByDeptno"
column="deptno">
</collection>
</resultMap>
<select id="findByDeptno" parameterType="int" resultMap="deptMap">
select * from dept where deptno=#{deptno}
</select>
</mapper>
<mapper namespace="com.wxb.mapper.EmpMapper">
<select id="findEmpsByDeptno" parameterType="int" resultType="emp">
select * from emp where deptno=#{deptno}
</select>
</mapper>
3、编写接口
public interface EmpMapper {
public List<Emp> findEmpsByDeptno(@Param("deptno") int deptno);
}
public interface DeptMapper {
public Dept findByDeptno(@Param("deptno") int deptno);
}
4、测试
@Test
public void test1() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.findByDeptno(10);
logger.info(dept.getDname()+"\t"+dept.getLoc());
List<Emp> emps = dept.getEmps();
logger.info(dept.getDname()+"部门中共有"+dept.getEmps().size()+"个员工");
for(Emp e : emps){
logger.info(e.getEname()+"\t"+e.getHiredate()+"\t"+e.getJob());
}
MybaitsUtil.closeSqlSession(sqlSession);
}
C)collection分步查询的配置一对多的方式(双向一对多)
B讲的是在“一”的一方添加关系,双向就是在B的基础上给“多”的一方添加关系
1、实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
private int deptno;
private String dname;
private String loc;
private List<Emp> emps;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private int empno;
private String ename;
private String job;
private int mgr;
private Date hiredate;
private int sal;
private int comm;
private Dept dept; //=============================>主要是这里添加
}
2、mapper.xml
<mapper namespace="com.wxb.mapper.DeptMapper">
<resultMap id="deptMap" type="dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<!--
collection:在一方配置多方关联的节点,常用的属性如下
|- property: 关联对象的属性名
|-column:表示的是外键
-->
<collection property="emps"
select="com.wxb.mapper.EmpMapper.findEmpsByDeptno"
column="deptno">
</collection>
</resultMap>
<select id="findByDeptno" parameterType="int" resultMap="deptMap">
select * from dept where deptno=#{deptno}
</select>
<!-- ====================主要是这里添加=========== -->
<select id="findOneByDeptno" parameterType="int" resultType="dept">
select * from dept where deptno=#{deptno}
</select>
</mapper>
<mapper namespace="com.wxb.mapper.EmpMapper">
<!-- ====================主要是这里添加=========== -->
<resultMap id="empMap" type="emp">
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="mgr" column="mgr"></result>
<result property="hiredate" column="hiredate"></result>
<result property="sal" column="sal"></result>
<result property="comm" column="comm"></result>
<association property="dept"
select="com.wxb.mapper.DeptMapper.findOneByDeptno"
column="deptno">
</association>
</resultMap>
<select id="findEmpsByDeptno" parameterType="int" resultType="emp">
select * from emp where deptno=#{deptno}
</select>
<!-- ====================主要是这里添加=========== -->
<select id="findEmpByEmpno" parameterType="int" resultMap="empMap">
select * from emp where empno=#{empno}
</select>
</mapper>
3、接口
public interface DeptMapper {
public Dept findByDeptno(@Param("deptno") int deptno);
public Dept findOneByDeptno(@Param("deptno") int deptno);//=======>主要是这里添加
}
public interface EmpMapper {
public List<Emp> findEmpsByDeptno(@Param("deptno") int deptno);
public Emp findEmpByEmpno(@Param("empno") int empno);//=============>主要是这里添加
}
4、测试
@Test
public void test1() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.findEmpByEmpno(7369);
logger.info(emp.getEname()+"\t"+emp.getJob());
logger.info("在:"+emp.getDept().getDname()+"部门");
MybaitsUtil.closeSqlSession(sqlSession);
}
十一、动态SQL
A)、if和where实现多条件查询
场景:有多个查询条件(模糊查询姓名和工资)
1、接口
public List<Emp> findEmpsByCondition(@Param("ename") String ename,
@Param("sal") Integer sal);
2、mapper.xml
<select id="findEmpsByCondition" resultType="emp">
select * from emp
<where>
<if test="ename!=null and ename!=''">
and ename like concat('%',#{ename},'%')
</if>
<if test="sal !=null &&sal!=0">
and sal>#{sal}
</if>
</where>
</select>
3、测试
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void test1() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> emps = mapper.findEmpsByCondition("s", 1200);
for(Emp emp : emps){
logger.info(emp);
}
MybaitsUtil.closeSqlSession(sqlSession);
}
B)、if和set实现update操作
1、接口
public void update(Emp emp);
2、mapper.xml
<update id="update" parameterType="emp">
update emp
<set>
<if test="ename!=null and ename!=''">ename=#{ename},</if>
<if test="job!=null and job!=''">job=#{job},</if>
<if test="mgr!=null and mgr!=''">mgr=#{mgr},</if>
<if test="hiredate!=null and hiredate!=''">hiredate=#{hiredate},</if>
<if test="sal!=null and sal!=''">sal=#{sal},</if>
<if test="comm!=null and comm!=''">comm=#{comm}</if>
<if test="dept.deptno!=null and dept.deptno!=''">deptno=#{dept.deptno},</if>
</set>
where empno=#{empno}
</update>
3、测试
@Test
public void test2() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = new Emp();
emp.setEmpno(7369);
emp.setEname("史密斯");
Dept dept = new Dept();
dept.setDeptno(10);
emp.setDept(dept);
mapper.update(emp);
sqlSession.commit();
MybaitsUtil.closeSqlSession(sqlSession);
}
C)trim的使用
1、trim实现多条件查询
<select id="selectEmpsByCoditionWithTrim" resultType="emp">
SELECT * FROM emp
<trim prefix="where" prefixOverrides="and">
<if test="ename!=null and ename!=''">
AND ename LIKE CONCAT('%',#{ename},'%')
</if>
<if test="sal!=null&&sal!=0.0">
AND sal>#{sal}
</if>
</trim>
</select>
2、trim实现修改操作
<update id="updateWithTrim" parameterType="emp">
UPDATE emp
<trim prefix="set" suffix="where empno=#{empno}" suffixOverrides=",">
<if test="ename!=null and ename!=''">ename=#{ename},</if>
<if test="job!=null and job!=''">job=#{job},</if>
<if test="mgr!=null and mgr!=''">mgr=#{mgr},</if>
<if test="hiredate!=null and hiredate!=''">hiredate=#{hiredate},</if>
<if test="sal!=null and sal!=''">sal=#{sal},</if>
<if test="comm!=null and comm!=''">comm=#{comm},</if>
<if test="dept.deptno!=null and dept.deptno!=0">deptno=#{dept.deptno},</if>
</trim>
</update>
D)foreach的使用
1、数组
public List<Emp> findEmpsByEmpnos(int[] empnos);
<select id="findEmpsByEmpnos" resultType="emp">
select * from emp where empno in
<foreach collection="array" item="empnos" open="(" separator="," close=")">
<!-- collection的值为array -->
#{empnos}
</foreach>
</select>
@Test
public void testForeach() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
int[] empnos = {7369,7499};
List<Emp> emps = mapper.findEmpsByEmpnos(empnos);
for(Emp e : emps){
logger.info(e);
}
MybaitsUtil.closeSqlSession(sqlSession);
}
2、list
public List<Emp> findEmpsByEmpnos1(List<Emp> empnos);
<select id="findEmpsByEmpnos1" resultType="emp">
select * from emp where empno in
<foreach collection="list" item="empnos" open="(" separator="," close=")"> <!-- collection的值为list -->
#{empnos}
</foreach>
</select>
@Test
public void testForeach1() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
List empnos = new ArrayList<>();
empnos.add(7369);
empnos.add(7499);
List<Emp> emps = mapper.findEmpsByEmpnos1(empnos);
for(Emp e : emps){
logger.info(e);
}
MybaitsUtil.closeSqlSession(sqlSession);
}
3、map
public List<Emp> findEmpsByEmpnos2(Map empnos);
<select id="findEmpsByEmpnos2" resultType="emp">
select * from emp where empno in
<foreach collection="mkey" item="empnos" open="(" separator="," close=")"> <!-- collection的值随便起,但是要作为map的键 -->
#{empnos}
</foreach>
</select>
@Test
public void testForeach2() {
SqlSession sqlSession = MybaitsUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Map map = new HashMap();
List empnos = new ArrayList<>();
empnos.add(7369);
empnos.add(7499);
map.put("mkey",empnos);//=======>这里的键mkey就是对应的xml中的collection的值
List<Emp> emps = mapper.findEmpsByEmpnos2(map);
for(Emp e : emps){
logger.info(e);
}
MybaitsUtil.closeSqlSession(sqlSession);
}
E)bind
之前完成模糊查询,使用的是concat函数完成concat(’%’,#{ename},’%’),这个函数只能用在mysql中,为了适应多数据库平台,引入bind标签
public List<Emp> findEmpsByBind(@Param("ename") String ename);
<select id="findEmpsByBind" resultType="emp">
<bind name="_ename" value="'%'+ename+'%'"/>
select * from emp where ename like #{_ename}
</select>
F)SQL片段
<sql id="BASE_COLUMN">
empno,ename,sal
</sql>
<select id="findEmpsByBind" resultType="emp">
<bind name="_ename" value="'%'+ename+'%'"/>
select
<include refid="BASE_COLUMN"></include> <!-- 在这里引入了sql片段 -->
from emp where ename like #{_ename}
</select>
十二、mybatis的缓存
MyBatis缓存分为一级缓存和二级缓存,一级缓存属于sqlSession级别的缓存,是自带的,二级缓存属于Mapper级别的缓存,默认没有开启,如果使用由程序员来进行配置
A)一级缓存
四种情况下一级缓存会失效
第1种情况:不同sqlSession
第2种情况:如果在第一次和第二次之间完成更新操作(增加、删除、修改)的操作
第3种情况:完成刷新操作 sqlSession.clearCache();//刷新缓存
第4种情况:如果查询的id不一样
B) 二级缓存
1)在mybatis-config.xml文件中开启二级缓存
<!--开启缓存,默认为true,所以也可以不用写这一步-->
<setting name="cacheEnabled" value="true"></setting>
2)在Mapper映射文件中使用<cache>开启用二级缓存
<mapper namespace="com.woniuxy.dao.DeptMapper">
<!--开启二级缓存-->
<cache></cache>
</mapper>
提示:
使用二级缓存时,与查询结果映射的Java对象必须实现java.io.Serializable接口的序列化和反序列化操作,如果存在父类,其成员都需要实现序列化接口。实现序列化接口是为了对缓存数据进行序列化和反序列化操作,因此二级缓存数据存储介质多样,不一定在内存,有可能是硬盘或者远程服务器。
3)关于二级缓存中<cache>属性的解释
flushInterval:刷新间隔。可以被设置为任意的正整数,而且他们代表一个合理的毫秒形式的时间段,
默认情况下不设置,也就没有刷新间隔,缓存仅仅调用语句时刷新。
size:缓存数目。可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目,
默认值为1024
readOnly:只读。该属性可以被设置为true或者false。只读的缓存会给所有调用者返回缓存对象的相同实例,
因此这些对象不能被修改。这提供了很重要的性能优势。
可以读写的缓存会返回缓存对象拷贝(通过序列化)。这种方式会慢一些,但是安全,因此默认是false.
eviction:回收策略,默认为LRU。有如下几种。
LRU:最近最少使用的策略,移除最长时间不被使用的对象。
FIFO:先进先出策略,按对象进入缓存的顺序来移除它们。
SOFT:软引用策略,移除基于垃圾回收器状态和软引用规则的对象
WEAK:弱引用策略,更积极地移除基于垃圾收集器状态和弱引用规则的对象。