1.什么是Mybatis
一种持久层的框架,类似于JDBC访问数据库的操作
jdbc使用的是Connection对象,PreparedStatement对象,ResultSet对象
而mybatis框架的核心对象有SqlSessionFactoryBuilder对象和SqlSessionFactory对象和SqlSession对象等,并且mybatis框架较为灵活
2.为什么使用框架:
通过框架来制定开发规范,更进一步保证所有的开发人员能够快速编写统一的代码,让开发人员专注于业务实现
3.常用框架技术
3.1 Spring
Spring是一个J2EE的框架,这个框架提供了对轻量级IOC的良好支持,同时也提供了对AOP技术非常好的封装,相比于其他的框架,Spring框架的设计更加模块化,框架内的每个模块都能完成特定的工作,而且各个模块可以独立的运行,不会相互的前置,因此,在使用Spring框架的时候,我们可以使用整个框架,也可以使用框架中的一部分
主要学习:
IOC:控制反转---DI(依赖注入),spring容器化管理,取代new对象
AOP:面向切面,专门的人做专门的事
3.2 SpringMVC
Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
3.3 Mybatis
Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高
3.4Mybatis组成部分
核心对象:
SqlSessionFactoryBuilder; 用于生成SqlSessionFactory
SqlSessionFactory; 用于生成SqlSession
SqlSession; 用于执行sql语句
配置文件:
核心配置文件:mybatis-config.xml
sql映射文件:映射实体类和数据库中的表
4.mybatis小工程
4.1创建数据库:
CREATE TABLE `t_person` (
`id` int(32) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`nickname` varchar(32) DEFAULT NULL,
`age` int(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `t_person` VALUES ('1', 'zs', 'zz', '3');
INSERT INTO `t_person` VALUES ('2', 'lisi', 'll', '4');
INSERT INTO `t_person` VALUES ('3', 'wangwu', 'ww', '5');
创建一个数据库
4.2创建一个maven工程
包的结构路径,在src下面创建一个test文件夹,里面创建一个java文件夹,(test和main同级)设置为test类型
main中,和webapp同级的创建两个文件夹,一个java,一个resources,java设置问java文件夹,resources为资源文件夹
然后再java文件夹中创建包结构,域名.组织域名.工程名.包名。例如:cn.kgc.mybatis.entity实体类包
4.3加入依赖:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
mybatis jar包依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.36</version>
</dependency>
mysql 依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
4.4修改工程环境:
某些环境不更改,可能不会出现问题,可能和自己的环境有关
在pop.xml文件中将1.7修改成1.8
在project Structure中修改
在setting中搜索修改:
4.5创建实体类
Person.java在cn.kgc.pro_mybatis.entity中
加载lombokjar包后,添加@Getter和@Setter就默认生成getset方法
@Getter
@Setter
public class Person {
private Integer id;
private String name;
private String nickName;
private Integer age;
}
4.6创建mapper/PersonMapper接口
mapper接口相当于dao接口,不同的是不需要手动写BaseDao工具类和实现类,mybatis框架中自动实现
package cn.kgc.pro_mybatis.mapper;
public interface PersonMapper {
public Integer findCount();
}
4.7 创建mybatis映射文件resources.cn.kgc.mybatis.PersonMapper.xml
在resources中创建目录,和前面的尽量保持一致,保持一个相同的路径可以更容易理解
<?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="cn.kgc.pro_mybatis.mapper.PersonMapper">
<-- namespace,命名空间,是对应的mapper接口的包路径 -->
<-- 里面添加sql语句,id是mapper接口中的方法名,不可以同名, -->
<-- resultType是返回值类型的路径 -->
<select id="findCount" resultType="java.lang.Integer">
select count(*) from t_person
<-- sql语句 -->
</select>
</mapper>
4.8 创建mybatis主配置文件mybatis-config.xml
在resources目录下,创建主配置文件,mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-config.dtd">
<-- 文件头 -->
<configuration>
<-- 环境配置:数据库的连接信息 -->
<-- default:配置的值需要和某个enviroment的id相同,访问对应的数据库 -->
<environments default="development">
<environment id="development">
<-- mybatis的事务类型,选择JDBC -->
<transactionManager type="JDBC"></transactionManager>
<-- 数据源 -->
<dataSource type="POOLED">
<-- 四个属性 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_mybatis001"/>
<property name="username" value="root"/>
<property name="password" value="111"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn/kgc/pro_mybatis/mapper/PersonMapper.xml"/>
</mappers>
</configuration>
注意下面的mappers中,路径是\,不是.
而且在创建路径的时候,是\
用.创建会在mapper中添加resource后无法识别
4.9测试类TestMybatis.java
在test,java中创建一个测试类,利用三大对象进行测试
package cn.kgc.test;
import cn.kgc.entity.Person;
import cn.kgc.mapper.PersonMapper;
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 Test {
@org.junit.Test
public void testFindCount() throws IOException {
//通过Resources.getResourceAsStream读取主配置文件,以流的形式
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//实例化SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//通过实例化SqlSessionFactoryBuilder创建SqlSessionFactory
SqlSessionFactory build = builder.build(in);
//通过SqlSessionFactory对象的openSession方法创建SqlSession
SqlSession sqlSession = build.openSession();
//执行方法
Integer count = sqlSession.getMapper(PersonMapper.class).findCount();
System.out.println("count:" + count);
//关闭流对象
sqlSession.close();
}
}
可以看到运行结果:
总记录数:4
5.优化改进:
5.1加入日志配置:
在主配置文件中添加setting,添加日志信息
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
此时再次执行:可以看到具体执行的日志文件
这就是一个简单的mybatis的实现工程
5.2增删改等方法:
在上面测试了查询方法,现在可以试试增删改方法:
首先在mapper接口中添加方法,以增加为例子:
//增加
Integer addPerson(Person person);
sql映射文件:
注意:增删改查对应不同的标签,增删改没有返回值类型
<insert id="addPerson" parameterType="cn.kgc.mapper.PersonMapper">
insert into t_person(`name`,nickname,age) values (#{name},#{nickname},#{age})
</insert>
增insert,删delete,改update,查select
test测试:
注意:增删改需要先提交,commit(),先条件后关闭
@org.junit.Test
public void testAddPerson() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = builder.build(in);
SqlSession sqlSession = build.openSession();
Person person = new Person();
person.setName("yangchaoyue");
person.setNickname("杨超越");
person.setAge(22);
sqlSession.getMapper(PersonMapper.class).addPerson(person);
//提交事务
sqlSession.commit();
sqlSession.close();
}
5.3多参数要注意:
单个参数添加类型路径,多个参数建议封装
多个参数不封装的话加注解@Param("name"),在加入注解后,sql映射文件中就不需要添加参数类型
或者利用Map,用来存放数据
Person findByNameNickname(Person person);
Person findByNameNickname2(@Param("name") String name, @Param("nickname") String nickname);
Person findByNameNickname3(Map<String,Object> map);
对应的sql映射文件:
<select id="findByNameNickname" resultType="cn.kgc.entity.Person" parameterType="cn.kgc.entity.Person">
select * from t_person where `name`=#{name} and nickname=#{nickname}
</select>
<select id="findByNameNickname2" resultType="cn.kgc.entity.Person">
select * from t_person where `name`=#{name} and nickname=#{nickname}
</select>
<select id="findByNameNickname3" resultType="cn.kgc.entity.Person" parameterType="java.util.Map">
select * from t_person where `name`=#{name} and nickname=#{nickname}
</select>
5.4优化测试类,添加工具类
在测试的时候,可以看到,每个测试方法,都需要三大对象来创建,所以将相同的代码片段提取,封装成一个工具类
package cn.kgc.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 {
//定义私有的静态SqlSessionFactory对象
private static SqlSessionFactory factory = null;
//在类加载的时候执行,读取配置文件,创建一个SqlSessionFactory对象并赋值
static {
try {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession
public static SqlSession getSqlSession() {
SqlSession sqlSession = null;
if (factory != null) {
sqlSession = factory.openSession();
}
return sqlSession;
}
}
5.5sql语句中的#和$
在sql映射文件中,可以添加#或者$来进行值的操作
但是#是传递值
而$是替代字段
例如,在查询排序的时候,用两个不同的符号,会出现不同的效果,#无法排序
除此之外,#安全PreparedStatement,$不安全Statement
在不同的sql语句下查询的结果:
select * from t_person order by #{msg}
<== Row: 1, diliraba, 迪丽热巴, 18
<== Row: 2, yangmi, 杨幂, 19
<== Row: 3, wuxuanyi, 吴宣仪, 20
<== Row: 5, gulinazha, 古力娜扎, 17
select * from t_person order by ${msg}
<== Row: 5, gulinazha, 古力娜扎, 17
<== Row: 1, diliraba, 迪丽热巴, 18
<== Row: 2, yangmi, 杨幂, 19
<== Row: 3, wuxuanyi, 吴宣仪, 20
5.6将主配置文件的四个路径改成配置文件的方式:
将主配置文件中的死数据修改成活数据
在resources文件夹根目录下创建文件jdbc.properties,添加jdbc连接数据库的参数
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_mybatis001
username=root
password=111
在主配置文件中添加资源路径,并修改下方的参数
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn/kgc/mapper/PersonMapper.xml"/>
</mappers>
</configuration>
6.mybatis映射:
resultType和resultMap
在sql映射文件中,我们需要对每个方法的id,参数,方法进行设置,其中返回值在某些情况下返回的值和实体类中的字段不同,在不给别名的情况下,resultType无法设置,此时需要用到resultMap
resultType :直接表示返回类型,用在简单场景 ,基本数据类型
resultMap :复杂数据类型时候,用对外部resultMap的引用
应用场景:
数据库字段信息与对象属性不一致
复杂的联合查询,自由控制映射结果
二者不能同时存在,本质上都是Map数据结构
使用:
如图,t_user的rid是t_role的id,两张表关联查询,此时实体类User中的属性有
@Getter
@Setter
public class User {
private Integer id;
private String name;
private String password;
private Integer rid;
//t_role中的name,防止重名,修改名字
private String roleName;
}
在实体类中的名字为roleName对应的是b表中的name,此时有两种解决办法,
一个是修改sql语句,添加别名:
select a.id,a.name,a.password,b.name as roleName from t_user a,t_role b where a.rid=b.id and a.name=?;
此时就会一一对应,返回值类型用一个实体类接收,用resultType即可
1.实体类中有一条或少量基础数据类型
假如不使用别名可以用resultMap:
mapper接口:
//resultMap
User findResultMap(User user);
sql映射文件:
<select id="findResultMap" parameterType="cn.kgc.mybatis.entity.User" resultMap="resultMap">
select a.id,a.name,a.password,b.name from t_user a,t_role b where a.rid=b.id
and name = #{name}
</select>
<-- resultMap配置:前面property是实体类中的名字,后面column是数据库中的名字,给别名就不需要这个-->
<resultMap id="resultMap" type="cn.kgc.mybatis.entity.User">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<result property="password" column="password"></result>
<result property="roleName" column="name"></result>
</resultMap>
上面是最基础的resultMap:
2.实体类中有引用类型属性
当某次查询有多个别的类的属性的时候,一一添加多个属性或者多个别名会造成数据混乱,建议添加一个实体类(引用数据类型作为属性)
//将role表插入
private Role role;
mapper接口:
//利用user-role查询用户列表
List<User> findUserRole(Integer userRole);
sql映射文件:
<select id="findUserRole" parameterType="java.lang.Integer" resultMap="userRoleMap">
select a.id,a.userCode,b.roleName from smbms_user a,smbms_role b where a.userRole=b.id and a.userRole=#{userRole}
</select>
<resultMap id="userRoleMap" type="cn.kgc.mybatis003.entity.User">
<-- type是User路径,User中有Role role -->
<-- id是主键的意思,用result也可以,前面的字段都是第一张表的属性 -->
<id property="id" column="id"></id>
<result property="userCode" column="userCode"></result>
<-- association是其中的别的表的字段,type是那张表的路径,property是User属性中的Role的名字 -->
<association property="role" javaType="cn.kgc.mybatis003.entity.Role">
<result property="roleName" column="roleName"></result>
</association>
</resultMap>
3.实体类中有引用数据类型的集合
User实体类添加别的表的实体类,一对多,集合展示
private List<Address> addressList;
mapper接口:
//Collection查询
List<User> findCollection(@Param("id") Integer id);
sql:
<select id="findCollection" resultMap="collectionMap">
select a.id,a.userCode,b.addressDesc from smbms_user a,smbms_address b where a.id=b.userId and a.id=#{id}
</select>
<resultMap id="collectionMap" type="cn.kgc.mybatis003.entity.User">
<id property="id" column="id"></id>
<result property="userCode" column="userCode"></result>
<collection property="addressList" ofType="cn.kgc.mybatis003.entity.Address">
<result property="addressDesc" column="addressDesc"></result>
</collection>
</resultMap>
7.Mybatis动态sql
mybatis前面的查询语句都是定死的,但是当模糊查询的时候,某些字段可能有,可能没有,所以就要添加语句,让sql变成动态
7.1查询(if判断条件)
mapper接口:多字段模糊查询,实现输入不输入都能查询
//多字段模糊查询
List<User> findByNamePwd(@Param("name") String name, @Param("password") String password);
sql映射文件:
中间添加if,test中是判断条件,成功则拼接
<select id="findByNamePwd" resultType="cn.kgc.mybatis003.entity.User">
select * from smbms_user where 1 = 1
<if test="name!=null and name!=''">
and userName like concat("%",#{name},"%")
</if>
<if test="password!=null and password!=''">
and userPassword = #{password}
</if>
</select>
7.2 增加(trim去除最后的,)
//动态添加
Integer addDongTai(User user);
sql映射文件:
同样if判断,在if判断外面添加trim去除最后的,
<insert id="addDongTai" parameterType="cn.kgc.mybatis003.entity.User">
insert into smbms_user(
<trim suffixOverrides=",">
<if test="userCode != null and userCode != ''">
userCode,
</if>
<if test="userName != null and userName != ''">
userName,
</if>
</trim>
) values(
<trim suffixOverrides=",">
<if test="userCode != null and userCode != ''">
#{userCode},
</if>
<if test="userName != null and userName != ''">
#{userName},
</if>
</trim>
)
</insert>
当有userCode,没有userName的时候,sql语句就变成:
insert into smbms_user(userCode) values(#{userCode})
3.修改(trim中的别的属性)
//动态修改
Integer updateUserNew(User user);
suffixOverrides后缀覆盖
suffix后缀添加
prefix前缀添加
不同的属性都是为了拼接sql语句
<update id="updateUserNew" >
update smbms_user
<trim suffixOverrides="," suffix="where id=#{id}" prefix="set">
<if test="userCode != null and userCode != ''">
userCode=#{userCode},
</if>
<if test="userName != null and userName != ''">
userName=#{userName},
</if>
</trim>
</update>
7.4参数是列表:(foreach)
当参数是一个列表的时候:
//动态参数为list查询
List<User> findByList(List<BigInteger> id);
sql映射文件:
collection是list,代表类型
open是开头
close是结尾
item参数,是传入的值
separator是间隔符号
<select id="findByList" resultType="cn.kgc.mybatis003.entity.User">
select * from smbms_user where id in
<foreach collection="list" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
7.5,当参数是一个map<String,Object>,其中Object是一个列表类型的时候
//动态参数为map查询
List<User> findByMap(Map<String , Object> map);
和list的类似,但是collection不同,相当于key
<select id="findByMap" resultType="cn.kgc.mybatis003.entity.User">
select * from smbms_user where id in
<foreach collection="ids" open="(" close=")" item="map" separator=",">
#{map}
</foreach>
and userPassword in
<foreach collection="userPassword" open="(" close=")" item="map" separator=",">
#{map}
</foreach>
</select>
测试类:
@org.junit.Test
public void testFindByMap() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
List<Integer> list = new ArrayList<>();
list.add(16);
list.add(17);
List<Integer> list1 = new ArrayList<>();
list1.add(123);
list1.add(123456);
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("ids",list);
hashMap.put("userPassword",list1);
List<User> users = sqlSession.getMapper(UserMapper.class).findByMap(hashMap);
for (User user:users){
System.out.println(user.getUserName());
}
sqlSession.close();
}