1、认识mybatis
现在操作数据库使用的是:jdbc技术,比较麻烦,操作步骤多
mybatis是一个简化jdbc操作的一个框架。
什么是框架?框架是程序的一个半成品,无需程序员进行设计,只需要在框架的基础上完成自己的功能即可,简化了开发,提升开发效率
就像简历的模板
由于java是开源的,所以框架很多,在同一个技术上会出现多个框架
ORM框架是一类简化JDBC技术的框架的统称
O:Object 对象 java中的对象
R: Relationship 关系 数据库中的表
M: mapping 映射(关联)
ORM框架的原理:通过把java中的对象和数据库中的表进行关联后,从而进行JDBC简化操作的框架
ORM:mybatis hibernate jdbctemplate 等
mybatis框架的特点:
1、基于sql语句的框架
2、轻量级的半自动的框架(hibernate全自动,无需编写sql)
3、依赖数据库(不同数据库下,有不同的SQL语句)
4、对JDBC封装的持久化ORM框架
2、搭建mybatis的环境
1、新建maven项目
2、添加jar依赖
mysql mybatis
<dependencies>
<!--MyBatis核心依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--MySql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
3、编写配置文件 mybatis-cnfig.xml
需要有文件头,是mybatis官方提供的
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
mybatis-3-config.dtd是一个xml格式的文件,其作用就是限制引入该dtd文件的文件的格式
例如:必须包含哪些标签,标签的名字、顺序等
<?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>
<!--JDBC环境配置:配置数据库连接信息-->
<environments default="mysqlDB">
<!--一个environment就是一个数据库的连接信息-->
<environment id="mysqlDB">
<!--事务管理器的类型
JDBC,关键字:JDBC自身的事务管理器
-->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据源:连接池信息
以后配置Druid连接池
-->
<dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">
<!--配置驱动-->
<!--
property 指的是对应类的属性信息PooledDataSourceFactory
name里赋的值都是关键字
-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db2105?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<environment id="oracleDB">
<transactionManager type=""></transactionManager>
<dataSource type=""></dataSource>
</environment>
</environments>
</configuration>
3、使用mybatis
1、新建实体类
【注意】实体类的属性名最好和表中的列名要一致(不区分大小写)
package com.qf.mybatispro2105.pojo;
import java.io.Serializable;
public class Dept implements Serializable {
//确保无参构造方法和全参构造方法二选一
public Dept() {
}
public Dept(int deptNo, String dName, String loc) {
this.deptNo = deptNo;
this.dName = dName;
this.loc = loc;
}
private int deptNo;
private String dName;
private String loc;
public int getDeptNo() {
return deptNo;
}
public void setDeptNo(int deptNo) {
this.deptNo = deptNo;
}
public String getdName() {
return dName;
}
public void setdName(String dName) {
this.dName = dName;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
2、编写dao---接口
package com.qf.mybatispro2105.dao;
import com.qf.mybatispro2105.pojo.Dept;
public interface DeptDao {
//添加的方法
int addDept(Dept dept);
}
3、编写接口所对应的映射文件
mybatis在使用过程中有两种方式:
方式1:基于接口的注解方式(用的少,不推荐)
方式2:基于接口的映射文件方式(推荐)
创建在resources目录下 DeptMapper.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mybatispro2105.dao.DeptDao">
<!--
【说明】
1、接口中的每一个方法,都要在映射文件中有与之对应的映射
2、接口自身是支持方法重载,如果用在mybatis中的接口就不允许方法重载
因为通过mybatis调用方法时,mybatis只通过方法名来调用,不看参数,所以方法重载会报错
-->
<!--
方法的映射:
id映射的是接口中方法的名字
parameterType 映射参数的类型即可,
注意:如果在mybatis的配置文件中没有过实体类信息,需要在此填写类的完整限定名,如果不写包名则找不到此类
目前:只支持单个参数
insert update delete 无需设置返回值类型,默认返回为int类型
#{} 表达式,作用是到方法的形参中获取值,如果方法的形参是一个对象,那么获取的是对象的属性名
-->
<insert id="addDept" parameterType="com.qf.mybatispro2105.pojo.Dept">
<!--编写sql-->
insert into dept(deptno,dname,loc)
values
(#{deptNo},#{dName},#{loc})
</insert>
</mapper>
4、把映射文件要注册到mybatis的配置文件中
mybatis-config.xml添加配置如下
<mappers>
<mapper resource="DeptMapper.xml"></mapper>
</mappers>
mysql数据库自动提交事务---每条语句就是一个事务
mybatis关掉了mysql中自动提交事务的功能,需要手动提交事务
事务的本质:数据库缓存技术,执行到了缓存中
手动通过代码去控制事务称为编程式事务,有缺点:控制事务提交、回滚的代码分散在各个代码中
5、测试
package com.qf.mybatispro2105.test;
import com.qf.mybatispro2105.dao.DeptDao;
import com.qf.mybatispro2105.pojo.Dept;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class TestDept {
public static void main(String[] args) {
//mybatis的工作流程
SqlSession sqlSession=null;
try {
//1、通过流读取mybatis的配置文件信息
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//、建SqlSessionFactoryBuilder,目的是为了创建SqlSessionFactory
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//3、构建出SqlSessionFactory,目的是为了创建SqlSession对象
SqlSessionFactory factory=builder.build(inputStream);
//4、创建SqlSession对象
sqlSession=factory.openSession();
//mybatis是通过sqlSession操作数据库的
//通过SqlSession对象得到接口的实现类对象(代理对象---mybatis内部使用了代理设计模式)
DeptDao deptDao=sqlSession.getMapper(DeptDao.class);
//调用方法
Dept dept=new Dept(9010,"java2105部","设计城906室");
int result=deptDao.addDept(dept);
//手动提交事务
sqlSession.commit();
if(result==1){
System.out.println("添加部门成功");
}else{
System.out.println("添加部门失败");
}
} catch (IOException e) {
e.printStackTrace();
//回滚事务
sqlSession.rollback();
}
}
}
测试的方法:了解
package com.qf.mybatispro2105.test;
import com.qf.mybatispro2105.pojo.Dept;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class TestDept2 {
public static void main(String[] args) throws IOException {
InputStream inputStream= Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(inputStream);
SqlSession sqlSession=factory.openSession();
Dept dept=new Dept(9011,"aaa","aaaaaa");
//直接调用sqlSession对象的方法
try{
int result= sqlSession.insert("com.qf.mybatispro2105.dao.DeptDao.addDept",dept);
sqlSession.commit();
if(result==1){
System.out.println("添加部门成功!");
}else{
System.out.println("添加部门失败!");
}
}catch (Exception ex){
sqlSession.rollback();
}
}
}
4、细化mybatis的配置
原因:在映射文件中直接使用了类名,没有添加包名,而且,在mybatis的配置文件中也没有进行实体类的配置
1、1:配置别名
<!--配置别名:指定实体类的名字,在mapper文件中就可以直接使用实体类的名字-->
<typeAliases>
<!--缺点:需要逐个设置每个实体类-->
<!--<typeAlias type="com.qf.mybatispro2105.pojo.Dept"></typeAlias>-->
<!--告知mybatis扫描实体类的包,此包下的所有实体类都会被mybatis扫描到,进而识别到-->
<package name="com.qf.mybatispro2105.pojo"/>
</typeAliases>
typeAliases的配置做了两件事
1、告知实体类的位置
2、给实体类命名别名,默认情况下别名就是类名的首字母小写
1、2:配置数据库信息
1、创建db.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db2105pro?useUnicode=true&characterEncoding=utf-8
username=root
password=root
2、在mybatis配置文件的开始,引入db.properties文件
<!--引入properties文件-->
<properties resource="db.properties"></properties>
3、修改数据库连接信息的配置,使用${}读取db.properties文件的内容
<?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文件-->
<properties resource="db.properties"></properties>
<!--配置别名:指定实体类的名字,在mapper文件中就可以直接使用实体类的名字-->
<typeAliases>
<!--缺点:需要逐个设置每个实体类-->
<!--<typeAlias type="com.qf.mybatispro2105.pojo.Dept"></typeAlias>-->
<!--告知mybatis扫描实体类的包,此包下的所有实体类都会被mybatis扫描到,进而识别到-->
<package name="com.qf.mybatispro2105.pojo"/>
</typeAliases>
<!--JDBC环境配置:配置数据库连接信息-->
<environments default="mysqlDB">
<!--一个environment就是一个数据库的连接信息-->
<environment id="mysqlDB">
<!--事务管理器的类型
JDBC,关键字:JDBC自身的事务管理器
-->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据源:连接池信息
以后配置Druid连接池
-->
<dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">
<!--配置驱动-->
<!--
property 指的是对应类的属性信息PooledDataSourceFactory
name里赋的值都是关键字
-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<environment id="oracleDB">
<transactionManager type=""></transactionManager>
<dataSource type=""></dataSource>
</environment>
</environments>
<!--注册映射文件信息-->
<mappers>
<mapper resource="DeptMapper.xml"></mapper>
</mappers>
</configuration>
1、3:配置日志文件
在开发过程中,想了解mybatis的执行过程,mybatis内部使用了log4j日志框架,来记录mybatis的工作过程,
可以通过设置log4j,把mybatis的运行过程打印到控制台上,便于调试程序
log4j具体内容,后面会具体学习,只会配置即可
1、创建log4j.properties 文件,【注意】必须是这个名字
2、复制进去配置信息
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 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
3、添加log4j的jar依赖
<!-- log4j日志依赖 https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
4、在使用的时候,控制台打印mybatis的信息
1、4:配置mapper文件的存储位置
原因:
1、没有注册到mybatis的配置文件里
2、路径存储错误,默认到resources路径下查找映射的mapper文件
在pom.xml文件的最后,添加如下配置
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>*.xml</include>
<include>**/*.xml</include><!--**/代表多级目录-->
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
在mybatis的配置文件中修改如下:
<mappers>
<!--到resources下去寻找-->
<mapper resource="com/qf/mybatispro2105/dao/DeptMapper.xml"></mapper>
</mappers>
把mapper文件的编码方式由UTF-8 改为utf8,mapper文件里的中文注释就会被支持
5、查询的实现
<!--查询功能
如果没有参数,则无需设置parameterType属性
select标签必须设置返回值类型
resultType 设置返回值类型:当查询的列名和实体类的属性名一致时,发生同名映射
mybatis如果有多个查询结果,默认返回List集合,只需通过resultType设置泛型即可
例如:查询出3条数据,mybatis就默认调用3次无参构造方法,创建3个Dept对象
对每一条数据的各个列,按照同名的原则找对象的对应属性进行赋值,最后存在List集合中
-->
<select id="findDept" resultType="Dept">
select deptno,dname,loc from dept
</select>
6、mybatis中的多参数处理
<!--多条件查询
当方法中有多个参数时,无需设置parameterType属性
多参方式1:通过参数的顺序来获取
-->
<select id="findDept1" resultType="Dept">
select deptno,dname,loc
from dept
where dname=#{arg0} and loc=#{arg1}
</select>
<!--
多参方式2:通过@Patam()里定义的参数别名来获取
-->
<select id="findDept2" resultType="Dept">
select deptno,dname,loc
from dept
where dname=#{dn} and loc=#{loc}
</select>
<!--
多参方式3:参数是map集合,可以把多个参数放在map集合中,#{}获取map集合的的key所对应的value即可
-->
<select id="findDept3" resultType="Dept">
select deptno,dname,loc
from dept
where dname=#{name} and loc=#{location}
</select>
<!--
多参方式4:对象传参,#{}获取对象的属性名
-->
<select id="findDept4" resultType="Dept" parameterType="Dept">
select deptno,dname,loc
from dept
where dname=#{dName} and loc=#{loc}
</select>
【建议】通常优先使用对象传参,其次使用注解传参@Param
import com.mb.cloudCar.entity.Car;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface CarDao {
//添加的方法
int addCar(Car car);
//修改的方法
int updateCar(Car car);
//删除的方法
int deleteCar(String carId);
//无条件查询
List<Car> findCar();
//多条件查询
List<Car> findCar1(String carId,String money);
List<Car> findCar2(@Param("ci")String carId,@Param("mo")String money);
List<Car> findCar3(Map<String,Object> map);
List<Car> findCar4(Car car);
//模糊查询
List<Car> findCar5(Car car);
}
<?xml version="1.0" encoding="UTF8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mb.cloudCar.dao.CarDao">
<insert id="addCar" parameterType="car">
insert into car(carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
displacement,model,datepurchase,datereg,status,deptcarperson,
finaltime,nextyeartime,power,totalmass,fare,onboarddata,
remarks,filledby,fillingtime)
values
(#{carId},#{carNumber},#{money},#{carType},#{dept},#{gas},#{safeMan},
#{yearTime},#{displacement},#{model},#{datepurchase},#{datereg},
#{status},#{deptCarperson},#{finalTime},#{nextYearTime},#{power},
#{totalMass},#{fare},#{onboarddata},#{remarks},#{filledby},#{fillingTime})
</insert>
<update id="updateCar" parameterType="com.mb.cloudCar.entity.Car">
update car set carnumber=#{carNumber},money=#{money},cartype=#{carType},dept=#{dept},
gas=#{gas},safeman=#{safeMan},yeartime= #{yearTime},displacement=#{displacement},
model=#{model},datepurchase=#{datepurchase},datereg=#{datereg},
status=#{status},deptcarperson=#{deptCarperson},finaltime=#{finalTime},
nextyeartime=#{nextYearTime},power=#{power},totalmass= #{totalMass},
fare=#{fare},onboarddata=#{onboarddata},remarks=#{remarks},
filledby=#{filledby},fillingtime=#{fillingTime}
where carid=#{carId}
</update>
<delete id="deleteCar" parameterType="String">
delete from car where carid=#{carId}
</delete>
<select id="findCar" resultType="Car">
select carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
displacement,model,datepurchase,datereg,status,deptcarperson,
finaltime,nextyeartime,power,totalmass,fare,onboarddata,
remarks,filledby,fillingtime
from car
</select>
<select id="findCar1" resultType="Car">
select carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
displacement,model,datepurchase,datereg,status,deptcarperson,
finaltime,nextyeartime,power,totalmass,fare,onboarddata,
remarks,filledby,fillingtime
from car
where carid=#{arg0} and money=#{arg1}
</select>
<select id="findCar2" resultType="Car">
select carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
displacement,model,datepurchase,datereg,status,deptcarperson,
finaltime,nextyeartime,power,totalmass,fare,onboarddata,
remarks,filledby,fillingtime
from car
where carid=#{ci} and money=#{mo}
</select>
<select id="findCar3" resultType="Car">
select carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
displacement,model,datepurchase,datereg,status,deptcarperson,
finaltime,nextyeartime,power,totalmass,fare,onboarddata,
remarks,filledby,fillingtime
from car
where carid=#{ci} and money=#{mo}
</select>
<select id="findCar4" resultType="Car" parameterType="Car">
select carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
displacement,model,datepurchase,datereg,status,deptcarperson,
finaltime,nextyeartime,power,totalmass,fare,onboarddata,
remarks,filledby,fillingtime
from car
where carid=#{carId} and money=#{money}
</select>
<select id="findCar5" resultType="Car" parameterType="Car">
select carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
displacement,model,datepurchase,datereg,status,deptcarperson,
finaltime,nextyeartime,power,totalmass,fare,onboarddata,
remarks,filledby,fillingtime
from car
where carid like CONCAT('%',#{carId},'%') and money like CONCAT('%',#{money},'%')
</select>
</mapper>
7、模糊查询
<select id="findDept5" resultType="Dept" parameterType="string">
select deptno,dname,loc
from dept
where loc like CONCAT('%',#{loc},'%')
</select>
分页查询
//分页查询
List<Difficulty> findDifficultyByPage(@Param("start")int start,@Param("size")int size);
//分页模糊查询总页数
int findDifficultyCon(@Param("difficultyName") String difficultyName);
//分页模糊查询
List<Difficulty> findDifficultyByLike(@Param("difficultyName")String difficultyName,
@Param("start")int start,
@Param("size")int size);
//分页模糊查询总页数
int findStageCon(@Param("stageName")String stageName ,@Param("subjectName")String subjectName);
//分页模糊查询
List<Stage> findStageByLike(@Param("stageName")String stageName,
@Param("subjectName")String subjectName,
@Param("start")int start,
@Param("size")int size);
<!--分页查询-->
<select id="findDifficultyByPage" resultType="Difficulty">
select difficultyno,difficultyname,score
from difficulty
limit #{start},#{size}
</select>
<!--分页查询总页数-->
<select id="findDifficultyCon" resultType="int">
select count(difficultyno)
from difficulty
where difficultyname LIKE concat('%',#{difficultyName},'%')
</select>
<!--分页模糊查询-->
<select id="findDifficultyByLike" resultType="Difficulty">
select difficultyno,difficultyname,score
from difficulty
where difficultyname LIKE concat('%',#{difficultyName},'%')
limit #{start},#{size}
</select>
<!--分页模糊查询总页数-->
<select id="findStageCon" resultType="int">
select count(stageid)
from `subject` s
INNER JOIN stage t ON s.subjectid=t.subjectid
where t.stagename LIKE CONCAT('%',#{stageName},'%')
and
s.subjectname LIKE CONCAT('%',#{subjectName},'%')
</select>
<!--分页模糊查询-->
<select id="findStageByLike" resultMap="stageMap">
select t.stageid,t.stagename,s.subjectname
from `subject` s
INNER JOIN stage t ON s.subjectid=t.subjectid
where t.stagename LIKE CONCAT('%',#{stageName},'%')
and
s.subjectname LIKE CONCAT('%',#{subjectName},'%')
limit #{start},#{size}
</select>
//分页查询
@GetMapping("/finddifficultybypage")
public Map findDifficultyByPage(int page,int limit){
List<Difficulty> list = difficultyService.findDifficultyByPage(page,limit);
Map<String,Object> map = new HashMap<String,Object>();
map.put("code","0");
map.put("msg","");
map.put("count",difficultyService.findDifficulty().size());
map.put("data",list);
return map;
}
//分页模糊查询
@GetMapping("/bylike")
public Map findDifficultyByLike(String difficultyName,int page,int limit){
int count = difficultyService.findDifficultyCon(difficultyName);
List<Difficulty> list = difficultyService.findDifficultyByLike(difficultyName,page,limit);
Map<String,Object> map = new HashMap<String,Object>();
map.put("code","0");
map.put("msg","");
map.put("count",count);
map.put("data",list);
return map;
}
//分页模糊查询
@GetMapping("/findStageByLike")
public Map findStageByLike(String stageName,String subjectName,int page,int limit){
//获取总记录数
int count = stageService.findStageCon(stageName,subjectName);
//获取当前页数据
List<Stage> list = stageService.findStageByLike(stageName,subjectName,page,limit);
Map<String,Object> map = new HashMap<String,Object>();
map.put("code","0");
map.put("msg","");
map.put("count",count);
map.put("data",list);
return map;
}
8、主键回填
就是获取刚才添加的时候所需要的那个id值
订单表:order
id 订单id 自增
ordertime 订单时间
userID 下单人
totalprice 总价
1 2022-4-1 taoxiankun 200
订单详情表:orderdetail
id 自增主键
orderid 订单编号
goodsid 商品编号
1 1 kele
2 1 keyboard
主从表结构:
主表:订单基本信息
从表:订单的商品信息
对于主从表结构,在添加时应该在一个事务内,先插入主表,获得自增主键,然后在插入从表是要使用这个主键
如何在插入一个表后,获得我插入的主键?
使用主键回填的方式
主键:int类型,自增主键回填
<insert id="addOrder" parameterType="Order">
<!--回填到Order对象的id属性里-->
<!--回填方式之一
keyProperty:回填到哪个属性中
resultType:指定回填主键的类型
order:在插入之前BEFORE,还是插入之后AFTER回填
-->
<selectKey keyProperty="id" resultType="int" order="AFTER">
<!--获取自增的主键-->
select LAST_INSERT_ID()
</selectKey>
insert into `order`(ordertime,totalprice,userid)
values
(#{orderTime},#{totalPrice},#{userId})
</insert>
<!--
useGeneratedKeys:告知Mybatis所执行的insert语句,会产生自动生成的主键值
keyProperty: 产生的哪一列值进行自增
keyColumn:该表在数据库中的主键
-->
<insert id="addOrder2" parameterType="Order" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
<!--主键回填方式2-->
insert into `order`(ordertime,totalprice,userid)
values
(#{orderTime},#{totalPrice},#{userId})
</insert>
【说明】
1、以上两种方式,只适用于int自增主键
2、本质是执行mysql的函数LAST_INSERT_ID()来获取的主键
主键:字符串类型,需要自己调用数据库的UUID函数,产生主键
顺序: 先产生主键,回填给实体类的属性
然后 执行insert ,获取刚才回填的主键
<?xml version="1.0" encoding="utf8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mybatispro2105.dao.Order2Dao">
<insert id="addOrder2" parameterType="Order2">
<selectKey keyProperty="id" resultType="String" order="BEFORE">
<!--借助数据库的UUID()产生字符串类型主键
回填给Order2对象的id属性
-->
select REPLACE(UUID(),'-','')
</selectKey>
insert into order2 (id,name)
values (#{id},#{name})
</insert>
</mapper>
9、封装一个工具类
原则:
1、复用
2、用好作用域
package com.qf.mybatispro2105.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
//SqlSessionFactoryBuilder:作用域是局部变量级别,构建完SqlSessionFactory,就可以销毁
//SqlSessionFactory:作用域时程序级别,是static,不断的创建sqlSession
//sqlSession:是会话级别的,访问数据库之前创建,访问数据库之后销毁
//声明session工厂
private static SqlSessionFactory factory;
//声明局部线程类,存放SqlSession
private static final ThreadLocal<SqlSession> tl=new ThreadLocal<SqlSession>();
static {
//读取配置文件
try {
InputStream inputStream= Resources.getResourceAsStream("mybatis-config.xml");
//局部
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
factory=builder.build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获得SqlSession的方法
private static SqlSession openSession(){
//到局部线程类中获取
SqlSession session=tl.get();
//判断是否获取到
if(session==null){
//则创建
session=factory.openSession();
//存入到局部线程类中
tl.set(session);
}
return session;
}
//关闭session
private static void closeSession(){
//到局部线程类里获取要关闭的sqlSession
SqlSession session=tl.get();
session.close();
//从局部线程类删除
tl.remove(); //删除当前线程ID的sqlsessiion
}
//提交事务
public static void commit(){
SqlSession sqlSession=MybatisUtil.openSession();
sqlSession.commit();
//关闭sqlSession
MybatisUtil.closeSession();
}
//回滚事务
public static void rollback(){
SqlSession sqlSession=openSession();
sqlSession.rollback();
closeSession();
}
//获取接口实现类对象的方法
public static <T extends Object> T getMapper(Class<T> clazz){
SqlSession sqlSession=openSession();
return sqlSession.getMapper(clazz);
}
}
测试工具类
package com.qf.mybatispro2105.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
//SqlSessionFactoryBuilder:作用域是局部变量级别,构建完SqlSessionFactory,就可以销毁
//SqlSessionFactory:作用域时程序级别,是static,不断的创建sqlSession
//sqlSession:是会话级别的,访问数据库之前创建,访问数据库之后销毁
//声明session工厂
private static SqlSessionFactory factory;
//声明局部线程类,存放SqlSession
private static final ThreadLocal<SqlSession> tl=new ThreadLocal<SqlSession>();
static {
//读取配置文件
try {
InputStream inputStream= Resources.getResourceAsStream("mybatis-config.xml");
//局部
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
factory=builder.build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获得SqlSession的方法
private static SqlSession openSession(){
//到局部线程类中获取
SqlSession session=tl.get();
//判断是否获取到
if(session==null){
//则创建
session=factory.openSession();
//存入到局部线程类中
tl.set(session);
}
return session;
}
//关闭session
private static void closeSession(){
//到局部线程类里获取要关闭的sqlSession
SqlSession session=tl.get();
session.close();
//从局部线程类删除
tl.remove(); //删除当前线程ID的sqlsessiion
}
//提交事务
public static void commit(){
SqlSession sqlSession=MybatisUtil.openSession();
sqlSession.commit();
//关闭sqlSession
MybatisUtil.closeSession();
}
//回滚事务
public static void rollback(){
SqlSession sqlSession=openSession();
sqlSession.rollback();
closeSession();
}
//获取接口实现类对象的方法
public static <T extends Object> T getMapper(Class<T> clazz){
SqlSession sqlSession=openSession();
return sqlSession.getMapper(clazz);
}
}
10、主键回填的应用
下订单时:写入订单表,订单详情表
要求:
1、在一个事务内
2、先插入订单主表,主键回填后,插入订单从表
在MVC分层下实现
dao:插入订单主表的方法---主键回填
插入订单从表的方法
service:控制事务,调用插入订单主表及插入订单从的dao方法
v: 构建商品集合
package com.qf.mybatispro2105.service.impl;
import com.qf.mybatispro2105.dao.OrderDao;
import com.qf.mybatispro2105.dao.OrderDetailDao;
import com.qf.mybatispro2105.pojo.Order;
import com.qf.mybatispro2105.pojo.OrderDetail;
import com.qf.mybatispro2105.service.OrderService;
import com.qf.mybatispro2105.util.DateUtil;
import com.qf.mybatispro2105.util.MybatisUtil;
import java.util.List;
public class OrderServiceImpl implements OrderService {
//获取dao操作对象
private OrderDao orderDao= MybatisUtil.getMapper(OrderDao.class);
private OrderDetailDao orderDetailDao=MybatisUtil.getMapper(OrderDetailDao.class);
public int setOrder(List<OrderDetail> details,String userId) {
//先生成订单
Order order=new Order();
order.setUserId(userId);
order.setTotalPrice(50);
order.setOrderTime(DateUtil.getTime());
int count=0;
try{
//插入订单
int orderResult=orderDao.addOrder2(order);
//经过一步执行,主键已经回填
//遍历插入订单从表
for(OrderDetail detail:details){
//把订单表里的订单编号,赋值给订单详情独享
detail.setOrderId(order.getId());
//插入订单详情
count+=orderDetailDao.addOrderDetail(detail);
}
//提交事务
MybatisUtil.commit();
}catch (Exception ex){
//回滚事务
MybatisUtil.rollback();
}
return count;
}
}
11、ORM映射
针对查询操作而言
【面试题】当查询结果的列名和实体类的属性名不一致时,该如何处理?
方式1:sql命名别名,让别名和实体类的属性名一致
private int dno; //deptNo
private String name; //dname
private String location; //loc
命名别名
<select id="findDept6" resultType="Dept2">
select deptno dno,dname `name`,loc location
from dept
</select>
【说明】在单表查询中,使用别名可以解决不同名的问题,但是在主外键关系的表中将不适用
方式2:使用resultMap进行ORM映射
使用resultMap
完成一对一的映射:
在类中有个属性,属性是单个对象类型
<?xml version="1.0" encoding="utf8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mybatispro2105.dao.EmpDao">
<!--编码进行映射
id是自定义的resultMap的唯一标识
type 要进行映射的实体磊
-->
<resultMap id="empMap" type="Emp">
<!--映射主键列时使用id标签-->
<id property="empNo" column="empno"></id>
<!--其余列使用result标签进行映射-->
<result property="eName" column="ename"></result>
<result property="jobs" column="job"></result>
<result property="com" column="com"></result>
<result property="sal" column="sal"></result>
<result property="joinDate" column="joindate"></result>
<result property="mgrNo" column="mgrno"></result>
<!--一对一映射:当属性为一个对象时,进行如下映射
mybatis会创建javaType类型的对应,然后按照column和property的映射赋值
把创建对象的地址赋值给属性 association property="dept"
-->
<!--映射dept对象-->
<!--<association property="dept" javaType="Dept">
<id property="deptNo" column="deptno"></id>
<result property="dName" column="dname"></result>
<result property="loc" column="loc"></result>
</association>-->
<association property="dept" javaType="Dept" resultMap="deptMap">
</association>
</resultMap>
<!--声明dept的映射-->
<resultMap id="deptMap" type="Dept">
<id property="deptNo" column="deptno"></id>
<result property="dName" column="dname"></result>
<result property="loc" column="loc"></result>
</resultMap>
<!--
当查询结果的列名或别名和实体类的属性名一致时,查询的返回值类型设置的是resultTpe
如果不符合以上条件,返回值类型设置为resultMap
二选一,不能同时存在
-->
<select id="findEmp" resultMap="empMap">
select empno,ename,job,com,sal,joindate,mgrno,emp.deptno,
dname,loc
from emp
inner join dept on emp.deptno=dept.deptno
</select>
</mapper>
完成一对多映射:
一个类中的属性是集合类型,称为一对多映射
<?xml version="1.0" encoding="utf8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mybatispro2105.dao.EmpDao">
<!--编码进行映射
id是自定义的resultMap的唯一标识
type 要进行映射的实体磊
-->
<resultMap id="empMap" type="Emp">
<!--映射主键列时使用id标签-->
<id property="empNo" column="empno"></id>
<!--其余列使用result标签进行映射-->
<result property="eName" column="ename"></result>
<result property="jobs" column="job"></result>
<result property="com" column="com"></result>
<result property="sal" column="sal"></result>
<result property="joinDate" column="joindate"></result>
<result property="mgrNo" column="mgrno"></result>
<!--一对一映射:当属性为一个对象时,进行如下映射
mybatis会创建javaType类型的对应,然后按照column和property的映射赋值
把创建对象的地址赋值给属性 association property="dept"
-->
<!--映射dept对象-->
<!--<association property="dept" javaType="Dept">
<id property="deptNo" column="deptno"></id>
<result property="dName" column="dname"></result>
<result property="loc" column="loc"></result>
</association>-->
<association property="dept" javaType="Dept" resultMap="deptMap">
</association>
</resultMap>
<!--声明dept的映射-->
<resultMap id="deptMap" type="Dept">
<id property="deptNo" column="deptno"></id>
<result property="dName" column="dname"></result>
<result property="loc" column="loc"></result>
</resultMap>
<!--
当查询结果的列名或别名和实体类的属性名一致时,查询的返回值类型设置的是resultTpe
如果不符合以上条件,返回值类型设置为resultMap
二选一,不能同时存在
-->
<select id="findEmp" resultMap="empMap">
select empno,ename,job,com,sal,joindate,mgrno,emp.deptno,
dname,loc
from emp
inner join dept on emp.deptno=dept.deptno
</select>
<!--一对多映射-->
<resultMap id="empMap2" type="Emp">
<!--映射主键列时使用id标签-->
<id property="empNo" column="empno"></id>
<!--其余列使用result标签进行映射-->
<result property="eName" column="ename"></result>
<result property="jobs" column="job"></result>
<result property="com" column="com"></result>
<result property="sal" column="sal"></result>
<result property="joinDate" column="joindate"></result>
<result property="mgrNo" column="mgrno"></result>
<!--一对一映射-->
<association property="dept" javaType="Dept" resultMap="deptMap"></association>
<!--一对多映射-->
<collection property="addressList" ofType="Address" resultMap="addMap">
</collection>
</resultMap>
<!--定义地址映射-->
<resultMap id="addMap" type="Address">
<id property="id" column="id"></id>
<result property="userId" column="empno"></result>
<result property="phone" column="phone"></result>
<result property="code" column="code"></result>
<result property="content" column="content"></result>
</resultMap>
<select id="findEmp2" resultMap="empMap2">
select empno,ename,job,com,sal,joindate,mgrno,emp.deptno,
dname,loc,phone,code,content
from emp
inner join dept on emp.deptno=dept.deptno
left join address on emp.empno=address.userid
</select>
</mapper>
完成多对多映射
使用场景
学生表:student:stuno,stuname
课程表:course:courseno,coursename
学生和课程之间就是多对多的关系:一个学生可以选多门课程,一门课程也可以被多个学生选
中间表:选课表,通过中间表把学生和课程之间的多对多的关系进行体现
stu_counse: id stuno courseno
多对多查询3表联查
Students实体类
package com.qf.mybatispro2105.pojo;
import java.util.List;
public class Students {
public Students() {
}
public Students(String stuNo, String stuName) {
this.stuNo = stuNo;
this.stuName = stuName;
}
public Students(String stuNo, String stuName, List<Course> courseList) {
this.stuNo = stuNo;
this.stuName = stuName;
this.courseList = courseList;
}
private String stuNo;
private String stuName;
//所选课程的属性
//一对多
private List<Course> courseList;
public List<Course> getCourseList() {
return courseList;
}
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
public String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
}
Course实体类
package com.qf.mybatispro2105.pojo;
import java.util.List;
public class Course {
public Course() {
}
public Course(String courseNo, String courseName) {
this.courseNo = courseNo;
this.courseName = courseName;
}
public Course(String courseNo, String courseName, List<Students> studentsList) {
this.courseNo = courseNo;
this.courseName = courseName;
this.studentsList = studentsList;
}
private String courseNo;
private String courseName;
//选本门课的学生
//一对多
private List<Students> studentsList;
public List<Students> getStudentsList() {
return studentsList;
}
public void setStudentsList(List<Students> studentsList) {
this.studentsList = studentsList;
}
public String getCourseNo() {
return courseNo;
}
public void setCourseNo(String courseNo) {
this.courseNo = courseNo;
}
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
}
由于是双向的一对多关系,就是多对多的关系
查询:
1、查询学生,同时查询学生所选课程
<?xml version="1.0" encoding="utf8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mybatispro2105.dao.StudentDao">
<resultMap id="stuMap" type="Students">
<id property="stuNo" column="stuno"></id>
<result property="stuName" column="stuname"/>
<collection property="courseList" ofType="Course" resultMap="courseMap"></collection>
</resultMap>
<resultMap id="courseMap" type="Course">
<id property="courseNo" column="courseno"/>
<result property="courseName" column="coursename"/>
</resultMap>
<select id="findStudents" resultMap="stuMap">
select s.stuno,stuname,c.courseno,c.coursename
from students s
left join stucourse sc on s.stuno=sc.stuno
inner join course c on c.courseno=sc.courseno
</select>
</mapper>
2、查询课程,同时查询选择该课程的学生
<?xml version="1.0" encoding="utf8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mybatispro2105.dao.CourseDao">
<resultMap id="courseMap" type="Course">
<id property="courseNo" column="courseno"/>
<result property="courseName" column="coursename"/>
<collection property="studentsList" ofType="Students" resultMap="stuMap"/>
</resultMap>
<resultMap id="stuMap" type="Students">
<id column="stuno" property="stuNo"/>
<result property="stuName" column="stuname"/>
</resultMap>
<select id="findCourse" resultMap="courseMap">
select c.courseno,c.coursename,s.stuno,s.stuname
from course c
left join stucourse sc on c.courseno=sc.courseno
inner join students s on s.stuno=sc.stuno
</select>
</mapper>
12、动态sql
动态sql语句,SQL语句会根据条件进行动态的组装、拼接
java中的逻辑判断,也能使用sql语句的动态拼接,但是比较麻烦,mybatis提供了一些动态sql标签,简化编程。
动态sql的原理:ognl
动态SQL本质就是sql语句的拼接
每个动态标签都是sqlnode类,对标签进行判断、进行拼接
sql标签:把重复使用的sql或部分sql语句使用sql标签进行定义,需要的引入即可
<?xml version="1.0" encoding="utf8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="common">
<sql id="deptSql">
select deptno,dname,loc
from dept
</sql>
</mapper>
<!--声明复用部分的sql-->
<sql id="deptSql">
select deptno,dname,loc
</sql>
<select id="findDept7" resultType="Dept">
<include refid="deptSql"></include>
from dept
</select>
<select id="findDept8" resultType="Dept">
<include refid="common.deptSql"></include>
</select>
在外部xml文件中声明复用的sql,引入时:命名空间.sql的ID namespace.id
if标签:判断
<select id="findDept9" parameterType="Dept" resultType="Dept">
<include refid="common.deptSql"></include>
where 1=1
<if test="dName!=null">
and dname=#{dName}
</if>
<if test="loc!=null">
and loc=#{loc}
</if>
</select>
where标签 :相当于where关键字
<select id="findDept10" parameterType="Dept" resultType="Dept">
<include refid="common.deptSql">
</include>
<where>
<if test="dName!=null">
and dname=#{dName}
</if>
<if test="loc!=null">
and loc=#{loc}
</if>
</where>
</select>
【作用】
1、判断是否添加where关键字
2、过滤第一个条件前面的 and 或or等关键字
Set标签:在Update语句中使用
<update id="updateDept2" parameterType="Dept">
update dept
<set>
<if test="dName!=null">
dname=#{dName},
</if>
<if test="loc!=null">
loc=#{loc}
</if>
</set>
where deptno=#{deptNo}
</update>
【作用】
1、相当于update语句中的set关键字
2、过滤掉 多余的,
trim标签:用来替代where、set标签
替代where
<select id="findDept10" parameterType="Dept" resultType="Dept">
<include refid="common.deptSql">
</include>
<!-- <where>
<if test="dName!=null">
and dname=#{dName}
</if>
<if test="loc!=null">
and loc=#{loc}
</if>
</where>-->
<!--
prefix:为开头的关键字
prefixOverrides: 覆盖掉多余的and 或 or
-->
<trim prefix="where" prefixOverrides="and|or">
<if test="dName!=null">
and dname=#{dName}
</if>
<if test="loc!=null">
and loc=#{loc}
</if>
</trim>
</select>
替换set标签
<update id="updateDept2" parameterType="Dept">
update dept
<!--
<set>
<if test="dName!=null">
dname=#{dName},
</if>
<if test="loc!=null">
loc=#{loc}
</if>
</set>
-->
<!--
suffixOverrides:在每个条件的后面覆盖掉多余的,
-->
<trim prefix="set" suffixOverrides=",">
<if test="dName!=null">
dname=#{dName},
</if>
<if test="loc!=null">
loc=#{loc},
</if>
</trim>
where deptno=#{deptNo}
</update>
foreach标签:用于循环
参数 | 描述 | 取值 |
---|---|---|
collection | 容器类型 | list、array、map |
open | 起始符 | ( |
close | 结束符 | ) |
separator | 分隔符 | , |
index | 下标号 | 从0开始,依次递增 |
item | 当前项 | 任意名称(循环中通过 #{任意名称} 表达式访问) |
<delete id="deleteDept2">
delete from dept
where deptno in
<!--
collection:用来指定参数的类型 array-数组 list-list集合 map-map集合
open:开头的字符
close:结尾的字符
separator:分隔符
delete from dept where deptno in (9011,9012,9013)
item:就是定义的变量,用来存放从数组/集合中取出的值
-->
<foreach collection="array" open="(" close=")" separator="," item="dno">
#{dno}
</foreach>
</delete>
【补充】为了提升效率,一条sql插入多个值
int addOrderDetail2(List<OrderDetail> list);
-- 一条语句如何插入多行值,这样提升效率
insert into orderdetail(orderid,goodsid,num)
VALUES
(5,'test1',1),
(5,'test2',1),
(5,'test3',1)
<insert id="addOrderDetail2" parameterType="list">
insert into orderdetail(orderid,goodsid,num)
values
<foreach collection="list" item="detail" separator=",">
(#{detail.orderId},#{detail.goodsId},#{detail.num})
</foreach>
</insert>
13、mybatis的缓存技术(查询)
mybatis把从数据库中查询到的结果,给缓存到一个“地方”,下次再需要这个结果时,mybatis就不从数据库重新查询,而是到缓存里获取查询结果,目的是提高查询效率
mybatis根据缓存的地方不同,分为一级缓存、二级缓存
一级缓存:缓存在session中,是默认的缓存
当sqlsession没有关闭及更新的情况下缓存数据有效
sqlsession缓存失效的场景:
1、提交事务
2、close
3、flush
二级缓存:缓存在“文件中”,跨session, 本质是文件的序列化
【要求】实体类及相关内容必须实现Serializable接口
开启二级缓存配置:
在mybatis的配置文件中mybatis-config.xml
【注意】配置的顺序
<settings>
<setting name="cacheEnabled" value="true"/> <!--开启了二级缓存-->
</settings>
在mapper映射文件中还需要配置
<cache/> <!--当前mapper中的所有查询都使用二级缓存,查询结果都换在文件中-->
如果某个方法不使用二级缓存,做如下配置
<!-- useCache="false" 当前查询结果不进行二级缓存-->
<select id="findDept6" useCache="false" resultType="Dept2">
select deptno dno,dname `name`,loc location
from dept
</select>
二级缓存失效:服务重启,一次程序运行 过程中都有效
服务关闭后,二级缓存的文件被删除
第一个服务请求的速度要慢:原因是要初始化很多东西。
14、配置Druid连接池
各种连接池性能对比
JDBC-Conn Pool | 1 Thread | 2 threads | 5 threads | 10 threads | 20 threads | 50 threads |
---|---|---|---|---|---|---|
tomcat-jdbc | 1,269 | 1,378 | 2,029 | 2,103 | 1,879 | 2,025 |
DBCP | 2,324 | 5,055 | 5,446 | 5,471 | 5,524 | 5,415 |
BoneCP | 3,738 | 3,150 | 3,194 | 5,681 | 11,018 | 23,125 |
jboss-datasource | 4,377 | 2,988 | 3,680 | 3,980 | 32,708 | 37,742 |
C3P0 | 10,841 | 13,637 | 10,682 | 11,055 | 14,497 | 20,351 |
Proxool | 16,337 | 16,187 | 18,310(Ex) | 25,945 | 33,706(Ex) | 39,501 (Ex) |
综合对比:Druid连接池的性能最高
mybatis下如何配置Druid连接池:
1、添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
2、使用Druid连接池,替换mybatis自带的连接池
package com.qf.mybatispro2105.util;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
public class MyDruidDataSourceFactroy extends PooledDataSourceFactory {
//在无参构造函数中,使用Druid连接池替换掉mybatis自带的连接池
public MyDruidDataSourceFactroy() {
this.dataSource=new DruidDataSource();
}
}
3、修改配置文件
<dataSource type="com.qf.mybatispro2105.util.MyDruidDataSourceFactroy">
<!--配置驱动-->
<!--
property 指的是对应类的属性信息PooledDataSourceFactory
name里赋的值都是关键字
-->
<!--
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
-->
<!--Druid连接池有自己的属性-->
<property name="driverClass" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
15、mybatis分页插件的使用
【面试题】mybatis的分页插件如何使用?工作原理?
可以使用原始的分页方式
1、添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
2、在mybatis-config的配置文件中,引入分页插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
3、使用
//分页查询
public List<Dept> findDept(int currentPage,int pageSize){
//设置查询的分页
//参数1:当前页
//参数2:每页显示的记录数
PageHelper.startPage(currentPage,pageSize);
//得到所有部门数据
List<Dept> list=deptDao.findDept();
PageInfo<Dept> pageInfo=new PageInfo<Dept>(list);
//getList() 返回的就是当前页的数据
return pageInfo.getList();
}
【注意】在设置startPage之后的查询才会进行分页(对查询的sql拼接上Limit)
public Map<String,Object> findDept(int currentPage, int pageSize){
Map<String,Object> map=new HashMap<String, Object>();
//设置查询的分页
//参数1:当前页
//参数2:每页显示的记录数
PageHelper.startPage(currentPage,pageSize);
//得到所有部门数据
List<Dept> list=deptDao.findDept();
//PageInfo 可以获取 分页信息
PageInfo<Dept> pageInfo=new PageInfo<Dept>(list);
//getList() 返回的就是当前页的数据
map.put("data",pageInfo.getList());
//获取总页数
map.put("totalPages",pageInfo.getPages());
//获取记录数
map.put("size",pageInfo.getSize());
map.put("pre",pageInfo.getPrePage());
map.put("next",pageInfo.getNextPage());
return map;
}
简化的地方:
1、分页sql
2、无需自行计算总页数等信息
16、基于接口+注解的使用方式
mybatis两种使用方式:
1、接口+mapper映射文件 推荐 本质是代理模式实现
2、接口+注解 sql语句写在注解中 了解
public interface DeptDao2 {
//添加部门
@Insert("insert into dept(deptno,dname,loc)values(#{deptNo},#{dName},#{loc})")
int addDept(Dept dept);
//修改部门
@Update("update dept set dname=#{dName},loc=#{loc} where deptno=#{deptNo}")
int updateDept(Dept dept);
//删除部门
@Delete("delete from dept where deptno=#{deptNo}")
int deleteDept(int deptNo);
//查询
@Select("select deptno,dname,loc from dept")
List<Dept> findDept();
}
使用与单表的简单操作,如果多表连接查询、动态sql的使用上不能支持
17、懒加载、延迟加载
场景:查询员工信息,包括员工的部门信息(部门名字、部门位置等),但是在使用的时候,很少使用员工的部门信息,但是在查询员工信息时,却已经查询出
优化:查询时,只查询常用的员工信息,暂时不查询部门信息,当何时使用员工的部门信息时,现去查询部门信息
延迟加载:不能使用连接查询,连接查询一并都进行了查询
使用过程:
1、开启延迟加载
<!--设置-->
<settings>
<setting name="cacheEnabled" value="true"/> <!--开启了二级缓存-->
<setting name="lazyLoadingEnabled" value="true"/><!--开启懒加载-->
</settings>
2、查询主表
<select id="findEmpByEmpNo" resultMap="lazyEmpMap" parameterType="int" >
select empno,ename,job,com,sal,joindate,mgrno,deptno
from emp
where empno=#{empNo}
</select>
<resultMap id="lazyEmpMap" type="Emp">
<!--映射主键列时使用id标签-->
<id property="empNo" column="empno"></id>
<!--其余列使用result标签进行映射-->
<result property="eName" column="ename"></result>
<result property="jobs" column="job"></result>
<result property="com" column="com"></result>
<result property="sal" column="sal"></result>
<result property="joinDate" column="joindate"></result>
<result property="mgrNo" column="mgrno"></result>
<!--一对一 或一对多映射时完成懒加载操作-->
<!--写的是实体类里的数据类型 要写完整的包名和准备懒加载的方法-->
<association property="dept" javaType="Dept"
select="com.qf.mybatispro2105.dao.DeptDao.findDeptByNo" column="deptno">
<id property="deptNo" column="deptno"/>
<result property="loc" column="loc"/>
<result property="dName" column="dname"/>
</association>
</resultMap>
3、懒加载查询方法
<select id="findDeptByNo" resultType="Dept" parameterType="int">
select deptno,dname,loc from dept where deptno=#{deptNo}
</select>
18、mybatis的执行器---扩充
执行器:executor sqlsession通过执行器executor来执行sql语句
mybatis的executor分为以下下3种:
1、ExecutorType.SIMPLE:简单执行器,每次使用时创建,使用完毕销毁
2、ExecutorType.REUSE:可重复使用执行器,每次使用时创建,创建完毕后,存储在map集合中,使用后不销毁,下次继续使用
3、ExecutorType.BATCH:批处理执行器,一次可执行多条语句
如何指定执行器?
19、总结
【总结】
映射文件的编写:namespace---关联接口
insert update delete 对应着接口中的方法
id---方法名 parameterType ---参数类型(单参、自定义类需要完整限定名) 无需设置返回值类型
#{} 取值---直接去对应参数名称的值,取对象的属性值
先编译sql 然后赋值, 根据值的数据类型自动单引号---preparestatement对象
${} 取值 使用的statement对象
先赋值,不添加单引号 然后编译 不安全
【面试题】#{} 和${}的区别?
重点:mybatis的工作流程?
1、创建SqlSessionFactoryBuilder 对象,来创建SqlSessionFactory
2、读取mybatis的配置文件信息
3、创建SqlSessionFactory对象 builder.build(inputstream);
用来创建SqlSession
4、得到SqlSession对象
SqlSessionFactory.openSession();
【解释】java程序每次与数据进行连接,执行sql语句,返回结果的一完整过程,在mybatis就是一次会话
每次操作数据库都应该重新获取sqlSession,操作结束后,应该关闭sqlSession