一.环境统一
1.jdk1.8
2.mysql5.7
3.maven3.6.1/3.0.0
4.IDEA
二.学习mabatis前需要掌握的
1.JDBC
2.Mysql
3.java基础
4.Maven
5.Junit
三.如何学习
1.ssm框架,每一个框架都有官方文档,最好的方式就是看官方文档
2.mybatis:https://mybatis.org/mybatis-3/zh/index.html
四.是什么
mybatis最开始是apache的开源项目ibatis,后来迁移到了google并改名为mybatis,再后来迁移到了github;
1.持久层框架
2.支持定制化sql,存储过程和高级映射
3.避免了JDBC的代码
4.避免手动设置参数和获取结果集
5.使用xml或者注解配置接口,pojo为数据库的记录
五.为什么用
1.传统的JDBC太复杂,为了简化JDBC出来的框架,有了这个框架,我们只需要在他的规则下往里面填东西就可以,是自动化的
2.为了把数据存到数据库中
3.不用mybatis也可以
4.优点:
<1>MyBatis 把 sql 放在 XML 文件中或注解方式编写,不用写在代码里,完成了sql语句和代码的解耦合,提高了可维护性
<2>MyBatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 JavaBean 对象大大简化了 Java 数据库编程的重复工作。
<3>因为 MyBatis 需要程序员自己去编写 sql 语句,程序员可以结合数据库自身的特点灵活控制 sql 语句,因此能够实现比 Hibernate 等全自动 orm 框架更高的查询效率,能够完成复杂查询
六.怎么用
1.如何获取mybatis
<1>maven仓库
https://mvnrepository.com/search?q=mybatis
<2>github
https://github.com/search?q=mybatis
2.了解持久化与持久层
<1>持久化就是将程序的数据在持久状态和瞬时状态转化的过程
持久状态:数据放到数据库中,只要不删库,数据都在
瞬时状态:内存的特性是断电即失,所以需要持久化,把内存的数据放到数据库
<2>实现持久化的方式:早期的直接存在txt文本,后来io文件持久化,再后来数据库(JDBC)
<3>持久层就是用来做数据持久化,通过存到数据库可以实现数据持久化
<4>mybatis就是完成持久化的框架
3.入门案例以及CRUD实现(以下是查询demo,注意增删改需要提交事务)
<1>环境搭建
I.搭建数据库环境(数据库的建库建表)
II.新建一个普通的maven项目(mybatis-study)
删除src目录把它当作父工程,pom文件中导入mybatis依赖和mysql驱动
<dependencies>
<!-- mysql驱动,连接数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
III.在父工程下新建子模块作为子项目
<2>编写核心配置文件以及工具类
I.编写xml核心配置文件
配置文件中写的是连接数据库用到的driver,url,username,password
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybaits?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT
jdbc.username=root
jdbc.password=88888888
<?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="jdbc.properties"></properties>
<!--设置java类型别名-->
<typeAliases>
<package name="com.itheima.pojo"></package>
</typeAliases>
<!--环境配置-->
<environments default="mysql">
<!--mysql环境-->
<environment id="mysql">
<!--使用JDBC类型事务管理器 -->
<transactionManager type="JDBC"></transactionManager>
<!--使用连接池 POOLED-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<package name="com.itheima.mapper"></package>
</mappers>
</configuration>
II.编写mybatis的工具类
从sqlsessionfactorybuilder/构造者设计模式中获取sqlsessionfactory/工厂设计模式,从工厂中获取sqlsession
i.为什么定义mybatis工具类
1)每一个mybatis应用都是以SqlSessionFactory(工厂模式)的实例为核心的,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder(建造者模式)创建,而SqlSessionFactoryBuilder可以从xml配置文件构建出SqlSessionFactory的实例
2)从SqlSessionFactory获取SqlSession的实例,使用SqlSession中执行数据库的sql命令
3)使用SqlSession调用xml或者注解定义的sql语句
ii.怎么定义mybatis工具类
怎么从SqlSessionFactory获取SqlSession
1)获取 SqlSessionFactory
2)从SqlSessionFactory获取SqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatisConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
<3>编写实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
<4>查询实现
I.dao层
public interface UserDao {
List<User> getUserList();
}
II.编写dao层(mapper)配置类与mapper绑定
xml文件中写sql语句crud,满足sqlsession的调用,注意增删改进行commit事务提交
<?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.itheima.mapper.UserMapper">
<select id="getUserList" resultType="com.itheima.pojo.User">
select * from mybatis.user
</select>
</mapper>
III.junit测试(使用sqlsession对象执行sql语句)
public class MyTest {
@Test
public void test(){
//第一步:获取sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//第二部:getmapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//第三步:关闭sqlsession
sqlSession.close();
}
}
IV.报错分析
i.mapper注册报错
ii.配置文件资源过滤问题
(maven中讲过,在pom文件中使用<build>...</build>)
<5>crud实现(需要提交事务)
I.接口编写
public interface UserMapper {
//查询全部用户
List<User> getUserList();
//根据id查询用户
User getUserById(int id);
//新增一个用户
int addUser(User user);
//修改一个用户
int updateUser(User user);
//删除一个用户
int deleteUser(int id);
}
II.映射文件编写sql语句
<?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.itheima.mapper.UserMapper">
<select id="getUserList" resultType="com.itheima.pojo.User">
select * from user
</select>
<select id="getUserById" resultType="com.itheima.pojo.User" parameterType="int">
select * from user where id = #{id}
</select>
<!--对象中的属性可以直接取出来-->
<insert id="addUser" parameterType="com.itheima.pojo.User">
insert into user (id,name,pwd) value (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="com.itheima.pojo.User">
update user set name = #{name},pwd = #{pwd} where id = #{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
III.测试类编写以及测试
i.查询指定id测试
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
ii.增加用户测试
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.addUser(new User(4,"rain","1111"));
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//增删改必须提交事务
sqlSession.commit();
sqlSession.close();
}
iii.更改用户测试
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.updateUser(new User(4,"老赵","2222"));
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//增删改必须提交事务
sqlSession.commit();
sqlSession.close();
}
iv.删除用户测试
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.deleteUser(4);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//增删改必须提交事务
sqlSession.commit();
sqlSession.close();
}
<6>.CRUD优化
I.万能map(优化传递的参数过多的情况)
万能map的使用(如果实体类或者数据库表的字段或者参数过多,考虑使用map
使用:map传递参数,直接在sql取出key就可以
好处:不需要知道数据库表里面有什么,sql随便自己写
II.模糊查询
<1>java代码执行的时候传递通配符,推荐这么写
<2>在sql拼接中使用通配符,注意sql注入问题
七.mybatis三个核心接口(mybatis的作用域和生命周期)
作用域和生命周期很重要,使用错误会导致并发问题
1.sqlsessionfactorybuilder:
一旦创建sqlsessionfactory就不再需要,作用域是局部变量
2.sqlsessionfactory:
可以理解为数据库连接池,一旦创建就一直运行,作用域是应用作用域(全局),实现方式是可以通过单例模式或者静态单例模式;多次创建会视为代码的坏味道(浪费内存)
3.sqlsession:
可以理解为连接到连接池的请求,需要开启和关闭,作用域是方法作用域,用完就关闭,否则资源被占用(代码实现是用try..catch)
4.mybatis的Demo执行流程
八.配置优化解析
0.位置顺序
1.核心配置文件: mybatis-config.xml
2.environments
<1>mybatis默认事务管理器是JDBC,连接池是POOLED
<2>environment
可以有多套环境,但是每一个SqlSessionFactory只能选择一种环境,默认development
transactionManager:
有两个,默认JDBC
dataSource:
数据源,用来连接数据库,学过的有dbcp,c3p0,druid(阿里的),Hikari(springboot集成的)
默认的是POOLED,池子,用完还可以拿来连接,作用就是为了web响应更快
3.properties
4.typeAliases
<1>直接使用<typeAlias>标签写别名
<2>也可以指定包名,Mybaits会在包名下搜索需要的JavaBean,默认别名就是这个实体类的类名,首字母小写 (也可以大写,默认小写)
<3>两种方式使用场景
i.实体类少用第一种,实体类多用第二种
ii.第一种可以直接在写xml核心配置文件的时候支持DIY别名,第二种想要实现DIY,需要在实体类上加注解@Alias(" ")
5.settings
常用设置:
<1>cacheEnabled
开启缓存
<2>lazyLoadingEnabled
开启懒加载,提高效率
<3>mapUnderscoreToCameICase
开启自动驼峰命名规则,把数据库表字段和实体类属性名做自动的映射(last_name -> lastName)
<4>logImpl
Mybatis的日志具体实现,支持的值有SEL4J/LOG4J...,为了打印日志
6.映射器(mappers)
MapperRegistry:注册绑定我们的mapper.xml文件
<1>使用资源路径(推荐)和class方式
i.mapper和mapper.xml必须同名
ii.mapper和mapper.xml必须在同一个包下,如果不在一个包,使用class方式不可能绑定注册,此时只能用资源路径mapper方式注册
<2>使用扫描包进行绑定
<3>没有注册绑定就会报错
7.其他配置
<1>typeHandlers(类型处理器)
<2>objectFactory(对象工厂)
<3>plugins插件
mybatis-gengerator-core
mybatis-plus
通用mapper
九.ResultMap(结果集映射)
1.为什么用
<1>解决实体类属性名和数据库表中字段名不一致的问题
<2>为什么会出现查不到,为null的结果
mybatis配置中的类型处理器会把pwd与实体类属性对应
<3>怎么解决(resultMap里只有result单一字段的解决方案)
i.用别名的方式把pwd as password,这样类型处理器就会把pwd转为password,就可以查询出结果
ii.resultmap解决
3.怎么用
实体类属性名和数据库表中字段名不一致就出现了冲突,有冲突就用resultmap解决
resultMap对于简单的语句不需要配置显示的结果集映射,对于复杂的语句描述关系就可以
<1>简单用法——单表操作(result元素)
<2>高级结果映射用法——多表操作(association/collection)
I.数据库设计
CREATE TABLE teacher (
id INT(10) NOT NULL,
name VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(id, name) VALUES (1, '秦老师');
CREATE TABLE student (
id INT(10) NOT NULL,
name VARCHAR(30) DEFAULT NULL,
tid INT(10) DEFAULT NULL,
PRIMARY KEY (id),
KEY fktid (tid),
CONSTRAINT fktid FOREIGN KEY (tid) REFERENCES teacher (id)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO student (id, name,tid) VALUES (1, '小明', 1);
INSERT INTO student (id, name,tid) VALUES (2, '小红', 1);
INSERT INTO student (id, name,tid) VALUES (3, '小张', 1);
INSERT INTO student (id, name,tid) VALUES (4, '小李', 1);
INSERT INTO student (id, name,tid) VALUES (5, '小王', 1);
II.多对一处理
多对一的理解:
多个学生对应一个老师
如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!
i.搭建测试环境
@Data
public class Student {
private int id;
private String name; //多个学生可以是同一个老师,即多对一
private Teacher teacher;
}
@Data
public class Teacher {
private int id;
private String name;
}
ii.接口编写
public interface TeacherMapper {
@Select("select * from teacher where id = #{tid}")
Teacher getTeacher(@Param("tid") int id);
}
public interface StudentMapper {
//获取所有学生及对应老师的信息
List<Student> getStudents();
}
iii.按查询嵌套处理(类似sql中的子查询)
①.sql语句编写
②mapper.xml配置文件
<select id="getStudents" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="Student">
<!--association关联属性 property属性名 javaType属性类型 column在多 的一方的表中的列名-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<!-- 这里传递过来的id,只有一个属性的时候,下面可以写任何值 association中column多参数配置:
column="{key=value,key=value}"
其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的 字段名。
-->
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
</select>
③测试
@Test
public void getTeacher(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudents();
for (Student student : students) {
System.out.println("学生名:"+student.getName()+
"——老师名:"+student.getTeacher().getName());
}
sqlSession.close();
}
iv.按结果嵌套处理(类似联表查询)
①.sql语句编写
②mapper.xml配置文件
<select id="getStudents" resultMap="StudentTeacher" >
select s.id sid, s.name sname , t.name tname
from student s,teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<!--关联对象property 关联对象在Student实体类中的属性-->
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
③测试
@Test
public void getTeacher(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudents();
for (Student student : students) {
System.out.println("学生名:"+student.getName()+
"——老师名:"+student.getTeacher().getName());
}
sqlSession.close();
}
IV.一对多处理
一对多的理解:
一个老师拥有多个学生
如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!
i.搭建环境
@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name; //一个老师多个学生
private List<Student> students;
}
ii.接口编写
public interface TeacherMapper {
//获取指定老师,及老师下的所有学生
Teacher getTeacher(@Param("id") int id);
}
iii.按结果嵌套处理(类似sql中的子查询)
①sql语句编写
②mapper.xml配置文件
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid, s.name sname , t.name tname, t.id tid
from student s,teacher t
where s.tid = t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<result property="tid" column="tid" />
</collection>
</resultMap>
③测试
@Test
public void testGetTeacher(){
SqlSession session = MybatisUtils.getSqlSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
}
iv.按查询嵌套处理
①sql语句编写
②mapper.xml配置文件
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id = #{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<!--column是一对多的外键 , 写的是一的主键的列名-->
<collection property="students" javaType="ArrayList"
ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{id}
</select>
③测试
@Test
public void testGetTeacher(){
SqlSession session = MybatisUtils.getSqlSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
}
十.日志
1.是什么
<1>数据库的操作出现异常,需要排错的时候,就用得到日志
<2>日志工厂LogImpl
Mybatis内置了很多的日志工厂,在settings配置中,具体使用哪一个日志实现,用setting指定
1)SLF4J
2)LOG4J (重点)
3) LOG4J2 :LOG4J加强版
4)STDOUT_LOGGING :控制台输出 (重点)
标准日志输出,不需要配置
5)JDK_LOGGING :java自带的日志工厂
2.为什么用
mapper映射文件中写sql语句,如果没有日志,sql语句写错只会在控制台报错,所以需要日志把错误打印出来来提高效率(之前是sout,debug,现在是日志工厂)
3.怎么用
<1>STDOUT_LOGGING
标准日志输出,在mybatis的xml核心配置文件中手动配置
<2>Log4j
I.Log4j概述
i.Log4j是Apache的开源项目(Tomcat,maven,Log4j都是Apache开源的),通过使用Log4j,可以控制日志信息输出的目的地是控制台,文件 ...
ii.可以控制每一条日志的输出格式
iii.可以通过定义日志信息级别把输出信息分等级输出
iv.可以通过配置文件灵活配置,不用修改应用的代码
II.导包并编写log4j.properties
i.先导包
ii.在项目的类路径下建立log4j.properties并且配置
III.在mybatis的xml核心配置文件中手动配置
xml核心配置文件中settings设置,配置log4j为日志的实现
IV.测试类使用
i.在要使用log4j的类中导包对应的包
ii.日志对象,参数为当前类的class,注意设置日志的级别
logger.info()相当于带权限的sout()
logger.debug()是在调试阶段使用的
logger.error()可以放在try..catch里面
V.实际使用
public class MyTest {
static Logger logger = Logger.getLogger(MyTest.class);
@Test
public void test(){
//第一步:获取sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//做一个提示信息
logger.info("info:进入test()方法成功");
//第二部:getmapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//第三步:关闭sqlsession
sqlSession.close();
}
}
十一.分页
使用分页是为了减少数据的处理量
1.limit分页
<1>编写接口以及mapper.xml写sql
public interface UserMapper {
//选择全部用户实现分页
List<User> getUserByLimit(Map<String,Integer>map);
}
<resultMap id="UserMap" type="User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
</resultMap>
<select id="getUserByLimit" parameterType="map" resultType="UserMap">
select * from user limit #{startIndex},#{pageSize}
</select>
<2>测试
@Test
public void getUserByLimit(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
}
2.RowBounds类实现分页
面向对象的特点,不建议在开发中用这种java的方式实现分页,还是推荐使用sql的方式
<1>编写接口以及mapper.xml写sql
public interface UserMapper {
//选择全部用户实现分页
List<User> getUserByRowBounds();
}
<resultMap id="UserMap" type="User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
</resultMap>
<select id="getUserByRowBounds" resultMap="UserMap">
select * from user
</select>
<2>测试
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds实现
RowBounds rowBounds = new RowBounds(0, 2);
//通过java代码层面实现分页
List<User> userList = sqlSession.selectList("com.itheima.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user : userList) {
System.out.println(user);
}
}
3.分页插件PageHelper
官网:https://pagehelper.github.io/docs/howtouse/
十二.注解开发
1.mybatis如何使用注解开发
mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了 新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建
2.sql 类型主要分类
@select ()
@update ()
@Insert ()
@delete ()
【注意】利用注解开发就不需要mapper.xml映射文件了 .
3.使用入门案例
<1>在接口上添加注解替代mapper.xml配置文件
public interface UserMapper {
//查询全部用户
@Select("select * from user")
List<User> getUserList();
}
<2>mybatis的核心配置文件绑定接口
<!--使用class绑定接口,之前是绑定配置类-->
<mappers>
<mapper class="com.itheima.mapper.UserMapper"/>
</mappers>
<3>测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
<4>注意
如果数据库表字段和实体类属性不一致,需要用到resultMap解决冲突,注解的方式就不能解决了,还需要用到xml配置文件
4.注解方式实现crud
<1>自动提交事务
sqlSessionFactory中的方法opensession()方法,通过方法重载,可以设置参数的方式自动提交事务,不需要在commit()
I.源码分析
II.代码实现
<2>@Param使用
方法需要传递多个参数时,参数类型是基本类型用这个注解,引用类型不用加
使用注解举例:
I.接口编写
public interface UserMapper {
//方法存在多个参数,所有的参数前面必须加上@Param("xxx")注解
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
}
II.测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
<3>crud实现(自动开启事务提交)
I.设置自动提交事务
II.接口编写
public interface UserMapper {
//添加一个用户
@Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})")
int addUser(User user);
//修改一个用户
@Update("update user set name=#{name},pwd=#{pwd} where id = #{id}")
int updateUser(User user);
//根据id删除用
@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id")int id);
}
III.测试
@Test
public void testAddUser() {
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(6, "join", "123456"); mapper.addUser(user);
session.close();
}
@Test
public void testUpdateUser() {
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(6, "join", "zxcvbn"); mapper.updateUser(user);
session.close();
}
@Test
public void testDeleteUser() {
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.deleteUser(6);
session.close();
}
5.#{}和${}区别
6.注解配合xml方式实现crud
<1>.结构目录
<2>.设置自动提交事务
<3>.接口编写以及mapper.xml配置文件编写
public interface UserMapper {
//查询全部用户
List<User> getUserList();
//模糊查询
List<User> getUserLike(@Param("value") String value);
//根据id查询用户
User getUserById(@Param("id") int id);
//map优化
List<User> getUserById2(Map<String, Integer> map);
//新增一个用户
int addUser(User user);
//map优化传参
int addUser2(Map<String, Object> map);
int addUser3(Map<String, Object> map);
//修改一个用户
int updateUser(User user);
//删除一个用户
int deleteUser(@Param("id") int id);
}
<4>.测试
参考上
十三.动态sql
1.是什么
动态sql就是根据不同的条件生成不同的sql语句
2.为什么用
简化JDBC拼接sql语句的痛苦
3.怎么用
<1>搭建环境
I.数据库环境搭建
CREATE TABLE blog (
id varchar(50) NOT NULL COMMENT '博客id',
title varchar(100) NOT NULL COMMENT '博客标题',
author varchar(30) NOT NULL COMMENT '博客作者',
create_time datetime NOT NULL COMMENT '创建时间',
views int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
II.编写核心配置文件
实体类驼峰命名属性与数据库表字段_命名自动转换的设置
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--下划线驼峰自动转换-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
III.编写工具类
i.编写mybatisutils工具类进行自动开启事务的管理
ii.编写idutils工具类为了生成随机id
public class IDUtils {
public static String genId(){
//这个工具类为了生成随机的id,而不是从1开始i++的id
return UUID.randomUUID().toString().replaceAll("-","");
}
}
IV.编写实体类
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
V.编写实体类对应的Mapper接口和Mapper.xml文件
public interface BlogMapper {
//新增一个博客
int addBlog(Blog blog);
}
<insert id="addBlog" parameterType="blog">
insert into blog (id, title, author, create_time, views)
values (#{id},#{title},#{author},#{createTime},#{views});
</insert>
VI.测试
@Test
public void addInitBlog(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtils.genId());
blog.setTitle("Mybatis如此简单");
blog.setAuthor("狂神说");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.addBlog(blog);
blog.setId(IDUtils.genId());
blog.setTitle("Java如此简单");
mapper.addBlog(blog);
blog.setId(IDUtils.genId());
blog.setTitle("Spring如此简单");
mapper.addBlog(blog);
blog.setId(IDUtils.genId());
blog.setTitle("微服务如此简单");
mapper.addBlog(blog);
session.close();
}
<2>常用标签
I.if
需求:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
i.接口编写以及mapper.xml编写sql
public interface BlogMapper {
//if标签
List<Blog> queryBlogIf(Map map);
}
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog where
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
ii.测试
@Test
public void addInitBlog() {
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java如此简单");
//map.put("author","狂神说");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
session.close();
}
iii.出现的问题
如果 author 等于 null,那么查询语句为 select * from user where title=#{title}, 但是如果title为空呢?那么查询语句为 select * from user where and author=#{author},这是错误的 SQL 语句,可以用where标签来解决
II.trim(where,set)
i.接口编写以及mapper.xml编写sql
//where标签
List<Blog> queryBlogWhere(Map map);
//set标签
int updateBlog(Map map);
<!--where标签-->
<select id="queryBlogWhere" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
<!--set标签-->
<!--注意set是用的逗号隔开-->
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if> </set>
where id = #{id};
</update>
ii.测试
public void queryBlogWhere() {
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java如此简单");
map.put("author","狂神说");
List<Blog> blogs = mapper.queryBlogWhere(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
session.close();
}
@Test
public void testUpdateBlog(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","动态SQL");
map.put("author","秦疆");
map.put("id","9d6a763f5e1347cebda43e2a32687a77");
mapper.updateBlog(map);
session.close();
}
III.choose(when、otherwise)
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
i.接口编写以及mapper.xml编写sql
List<Blog>queryBlogChoose(Mapmap);
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
ii.测试
@Test
public void testQueryBlogChoose(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title","Java如此简单");
map.put("author","狂神说");
map.put("views",9999);//这个条件必须有
List<Blog> blogs = mapper.queryBlogChoose(map);
System.out.println(blogs);
session.close();
}
IV.sql片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
比如if,where,set里面都有if的部分,就能抽取出来
V.foreach(遍历)
i.接口编写以及mapper.xml编写sql
public interface BlogMapper {
List<Blog> queryBlogForeach(Map map);
}
<!--
select * from blog where 1=1 and (id=1 or id=2 or id=3)
-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
-->
<foreach collection="ids" item="id" open="and (" close=")"
separator="or">
id=#{id}
</foreach>
</where>
</select>
ii.测试
@Test
public void testQueryBlogForeach(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap map = new HashMap();
List<String> ids = new ArrayList<String>();
ids.add("b3c46d9b58bb4b319f3746efe434b9ea");
ids.add("e24e72a4eaba4af8a7d131e8c26dbc90");
ids.add("b8942069301143a0a68c0299eae35686");
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
session.close();
}
十四.缓存Cache与mybatis缓存
1.缓存(读数据用得到,写数据不用缓存)
<1>是什么
存在内存中的临时数据就是缓存
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库 数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
<2>为什么用
连接数据库做查询的时候耗资源,我们想让一次查询的结果,可以暂存到一个可以直接取到的地方,这个地方就是内存,放在内存里查询的这些数据就是缓存,再次查询相同数据的时候,直接走缓存,不用走数据库(也就是不用再次连接数据库了)
<3>时候时候用
经常查询并且不经常改变的数据。
2.mybatis缓存
MyBatis包含一个非常强大的查询缓存特性,可以定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
<1>一级缓存
一级缓存也叫本地缓存
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
一级缓存的作用范围就是session的开启一直到关闭的这一段代码
I.接口以及对应的mapper.xml配置文件
public interface UserMapper {
//根据id查询用户
User queryUserById(@Param("id") int id);
}
<select id="queryUserById" resultType="user">
select * from user where id = #{id}
</select>
II.测试
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
System.out.println("===============================");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
III.缓存失效的场景
i.查询的内容不一样
①.接口编写以及mapper.xml编写sql
public interface UserMapper {
//根据id查询用户
User queryUserById(@Param("id") int id);
}
<select id="queryUserById" resultType="user">
select * from user where id = #{id}
</select>
②.测试
@Test
public void testCacheFail(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
System.out.println("===============================");
User user2 = mapper.queryUserById(2);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
ii.增删改
可能会改变原来的数据,必定会刷新缓存导致缓存失效
iii.手动清理缓存
@Test
public void testCacheFail2(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
session.clearCache();//手动清除缓存
System.out.println("===============================");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}