1简介
1.1什么是Mybits
- MyBatis 是一款优秀的持久层框架;
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1.2 什么是持久层
数据持久化
持久化就是将程序的数据在持久状态和瞬时状态转化过程。
内存:断电即失
数据库:io文件持久化
生活:冷藏
为什么需要持久化?
有一些对象不能让他丢掉,内存太贵了。
持久层
Dao层 service 层 controller层
完成持久化工作的代码块
层界限十分明显
1.3为什么需要Mybatis
方便
传统的JDBC代码太复杂,简化,框架,自动化。
帮助程序员将数据存入数据库中。
更容易上手。技术没有高低之分。
优点:
优点:
- 简单易学
- 灵活
- sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射。
- 提供对象关系映射标签,支持对象关系组建维护。
- 提供xml标签,支持编写动态sql。
最重要的一点:使用的人多。
2 第一个Mybatis程序
思路:搭建环境 –> 导入MyBatis –> 编写代码 –> 测试
新建项目:
1 新建一个普通meaven项目
2 删除src目录
3 导入meaven依赖
导入相关meaven依赖
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.21</version></dependency><!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency>
2.2 创建一个模块
编写核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments>
</configuration>
编写mybatis工具类
package com.kuang.utils;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 MybatisUtils {private static SqlSessionFactory sqlSessionFactory;static {//第一步:获取sqlSessionFactory对象String resource = "org/mybatis/example/mybatis-config.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);}public static SqlSession getSqlSession(){return sqlSessionFactory.openSession();}}
2.3 编写代码
实体类
package com.kuang.pojo;public class User {private int id;private String name;private String pwd;public User(int id, String name, String pwd) {this.id = id;this.name = name;this.pwd = pwd;}public User() {}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + ''' +", pwd='" + pwd + ''' +'}';}
}
Dao接口
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.dao.UserDao"><select id="getUserList" resultType="com.kuang.pojo.User">select * from USER</select>
</mapper>
接口实现类
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.dao.UserDao"><select id="getUserList" resultType="com.kuang.pojo.User">select * from USER</select>
</mapper>
测试代码
package com.kuang.dao;import com.kuang.pojo.User;
import com.kuang.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class TestMapper {public static void main(String[] args) {SqlSession sqlSession= MybatisUtil.getSqlSession();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);List<User> userList=userMapper.getUserList();for(User user:userList){System.out.println(user);}sqlSession.close();}
}
配置文件UserMapper.xml最好放在resources文件夹下
3CURD
1. namespace
namespace中的包名要和Dao/Mapper接口的包名一致
2. select
选择,查询语句;
-
id:就是对应的namespace中的方法名;
-
resultType : Sql语句执行的返回值;
-
parameterType : 参数类型;
3增删改需要提交事务
sqlSession.commit();
4万能Map
假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应该考虑使用Map
//用万能Map插入用户
public void addUser2(Map<String,Object> map);
别名是mybatis中的名字。
xml文件
<!--对象中的属性可以直接取出来 传递map的key-->
<insert id="addUser2" parameterType="map">insert into user (id,name,password) values (#{userid},#{username},#{userpassword})
</insert>
Map传递参数,直接在sql中取出key即可! 【parameter=“map”】
对象传递参数,直接在sql中取出对象的属性即可! 【parameter=“Object”】
只有一个基本类型参数的情况下,可以直接在sql中取到
多个参数用Map , 或者注解!
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.mapper.UserMapper"><select id="getUserList" resultType="com.kuang.pojo.User">select * from USER</select><insert id="addUser" parameterType="com.kuang.pojo.User">insert into user(id,name,pwd) values(#{id},#{name},#{pwd})</insert><insert id="addUser02" parameterType="map">insert into user(id,name,pwd) values(#{Id},#{Name},#{Pwd})</insert><delete id="deleteUserById" parameterType="int">delete from user where id=#{id}</delete><update id="updateUser" parameterType="com.kuang.pojo.User">update user set name=#{name},pwd=#{pwd} where id=#{id}</update><select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">select * from user where id=#{id}</select>
</mapper>
Usermapper的接口
package com.kuang.mapper;import com.kuang.pojo.User;import java.util.List;
import java.util.Map;public interface UserMapper {public List<User> getUserList();public int addUser(User user);public int addUser02(Map<String,Object>map);public int deleteUserById(int id);public int updateUser(User user);public User getUserById(int id);
}
测试代码
package com.kuang.dao;import com.kuang.mapper.UserMapper;
import com.kuang.pojo.User;
import com.kuang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;import java.util.HashMap;
import java.util.List;
import java.util.Map;public class UserDaoTest {@Testpublic void test(){SqlSession sqlSession= MybatisUtils.getSqlSession();UserMapper userMapper=sqlSession.getMapper(UserMapper.class);List<User> userList=userMapper.getUserList();for(User user:userList){System.out.println(user);}sqlSession.commit();sqlSession.close();}@Testpublic void testadd(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);int flag=mapper.addUser(new User(4,"王五","123456"));if(flag>0){System.out.println("插入成功");}else {System.out.println("插入失败");}sqlSession.commit();sqlSession.close();}@Testpublic void testadd02(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);Map<String,Object> map=new HashMap<String,Object>();map.put("Id",5);map.put("Name","hahaha");map.put("Pwd","1111111");int flag=mapper.addUser02(map);if(flag>0){System.out.println("插入成功");}else {System.out.println("插入失败");}sqlSession.commit();sqlSession.close();}@Testpublic void testDetele(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);int flag=mapper.deleteUserById(5);if(flag>0){System.out.println("删除成功");}else {System.out.println("删除失败");}sqlSession.commit();sqlSession.close();}@Testpublic void testUpdate(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);int flag=mapper.updateUser(new User(1,"HAHAHA","111111"));if(flag>0){System.out.println("更新成功");}else {System.out.println("更新失败");}sqlSession.commit();sqlSession.close();}@Testpublic void testGetUser(){SqlSession sqlSession=MybatisUtils.getSqlSession();UserMapper mapper=sqlSession.getMapper(UserMapper.class);User user=mapper.getUserById(1);System.out.println(user);sqlSession.close();}}
4 配置文件解析
1核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><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="org/mybatis/example/BlogMapper.xml"/></mappers>
</configuration>
configuration(配置)properties(属性)settings(设置)typeAliases(类型别名)typeHandlers(类型处理器)objectFactory(对象工厂)plugins(插件)environments(环境配置)environment(环境变量)transactionManager(事务管理器)dataSource(数据源)databaseIdProvider(数据库厂商标识)mappers(映射器)
2. 环境配置 environments
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境
学会使用配置多套运行环境!
MyBatis默认的事务管理器就是JDBC ,连接池:POOLED
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
数据源(dataSource)
不止一个类型
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
3. 属性 properties
我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.poperties】
如果两个文件都有相同属性,优先使用外部配置文件
注意properties和typeAliases标签的顺序
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><properties resource="db.properties"/><typeAliases><typeAlias type="com.kuang.pojo.User" alias="User"/></typeAliases><environments default="development"><environment id="development"><transactionManager type="JDBC"/><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="UserMapper.xml"/></mappers>
</configuration>
4. 类型别名 typeAliases
-
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置.
-
意在降低冗余的全限定类名书写。
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><typeAliases><typeAlias type="com.kuang.pojo.User" alias="User"/></typeAliases><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><mapper resource="UserMapper.xml"/></mappers>
</configuration>
<typeAliases><typeAlias type="com.kuang.pojo.User" alias="User"/></typeAliases>
也可以制定一个包名,MyBatis会在包名下面搜索需要的Java Bean,比如:
扫描实体类的包,它的默认别名就是为这个类的 类名,首字母小写
<typeAliases><typeAlias type="com.kuang.pojo.User"/>
</typeAliases>
在实体类比较少的时候,使用第一种方式,如果实体类十分多,建议使用第二种。
第一种可以自定义别名,第二种不能自定义别名,但是可以通过注解取一个别名。
5. 设置 Settings
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
一个配置完整的 settings 元素的示例如下:
<settings><setting name="cacheEnabled" value="true"/><setting name="lazyLoadingEnabled" value="true"/><setting name="multipleResultSetsEnabled" value="true"/><setting name="useColumnLabel" value="true"/><setting name="useGeneratedKeys" value="false"/><setting name="autoMappingBehavior" value="PARTIAL"/><setting name="autoMappingUnknownColumnBehavior" value="WARNING"/><setting name="defaultExecutorType" value="SIMPLE"/><setting name="defaultStatementTimeout" value="25"/><setting name="defaultFetchSize" value="100"/><setting name="safeRowBoundsEnabled" value="false"/><setting name="mapUnderscoreToCamelCase" value="false"/><setting name="localCacheScope" value="SESSION"/><setting name="jdbcTypeForNull" value="OTHER"/><setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
6. 其他配置
-
typeHandlers(类型处理器)
-
objectFactory(对象工厂)
-
plugins 插件
-
mybatis-generator-core
-
mybatis-plus
-
通用mapper
-
7. 映射器 mappers
MapperRegistry:注册绑定我们的Mapper文件;
方式一:【推荐使用】
<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers><mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册
<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers><mapper class="com.kuang.dao.UserMapper"/>
</mappers>
注意点:
-
接口和他的Mapper配置文件必须同名
-
接口和他的Mapper配置文件必须在同一个包下
方式三:使用包扫描进行注入
<mappers><package name="com.kuang.dao"/>
</mappers>
8. 作用域和生命周期
声明周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
-
一旦创建了SqlSessionFactory,就不再需要它了
-
局部变量
SqlSessionFactory:
-
说白了就可以想象为:数据库连接池
-
SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建一个实例。
-
因此SqlSessionFactory的最佳作用域是应用作用域(ApplocationContext)。
-
最简单的就是使用单例模式或静态单例模式。
SqlSession:
-
连接到连接池的一个请求
-
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
-
用完之后需要赶紧关闭,否则资源被占用!
5、解决属性名和字段名不一致的问题
1. 问题
数据库中的字段
新建一个项目,拷贝之前的,测试实体类字段不一致的情况
测试出现问题
// select * from user where id = #{id}
// 类型处理器
// select id,name,pwd from user where id = #{id}
起别名
2. resultMap
结果集映射
id name pwd
id name password
<!--结果集映射-->
<resultMap id="UserMap" type="User"><!--column数据库中的字段,property实体类中的属性--><result column="id" property="id"></result><result column="name" property="name"></result><result column="pwd" property="password"></result>
</resultMap><select id="getUserList" resultMap="UserMap">select * from USER
</select>
-
resultMap
元素是 MyBatis 中最重要最强大的元素。 -
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
-
ResultMap
的优秀之处——你完全可以不用显式地配置它们。 -
如果这个世界总是这么简单就好了。
6、日志
6.1 日志工厂
如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手!
曾经:sout、debug
现在:日志工厂
-
SLF4J
-
LOG4J 【掌握】
-
LOG4J2
-
JDK_LOGGING
-
COMMONS_LOGGING
-
STDOUT_LOGGING 【掌握】
-
NO_LOGGIN
在Mybaits中具体使用哪一个日志实现,在设置中设定。
<settings><setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
实质是一个JDBC Connection
6.2 Log4j
什么是Log4j?
-
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件;
-
我们也可以控制每一条日志的输出格式;
-
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程;
-
最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
-
导包
- 配置资源文件
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/jianghe.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sq1.PreparedStatement=DEBUG
-
配置settings为log4j实现
-
测试运行
Log4j简单使用
-
在要使用Log4j的类中,导入包 import org.apache.log4j.Logger;
-
日志对象,参数为当前类的class对象
Logger logger = Logger.getLogger(UserDaoTest.class);
-
1
-
-
日志级别
logger.info("info: 测试log4j"); logger.debug("debug: 测试log4j"); logger.error("error:测试log4j");
将信息输出到日志文件中
-
思考:为什么分页?
-
减少数据的处理量
-
7.1 使用Limit分页
-
SELECT * from user limit startIndex,pageSize
-
使用MyBatis实现分页,核心SQL
-
接口
//分页 List<User> getUserByLimit(Map<String,Integer> map);
-
Mapper.xml
<!--分页查询--> <select id="getUserByLimit" parameterType="map" resultMap="UserMap">select * from user limit #{startIndex},#{pageSize} </select>
-
测试
@Testpublic void getUserByLimit(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);HashMap<String, Integer> map = new HashMap<String, Integer>();map.put("startIndex",1);map.put("pageSize",2);List<User> list = mapper.getUserByLimit(map);for (User user : list) {System.out.println(user);}}
-
-
7.2RowBounds分页
7.3分页插件
8、使用注解开发
8.1 面向接口开发
三个面向区别
-
面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性和方法;
-
面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现;
-
接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构;
8.2 使用注解开发
-
注解在接口上实现
@Select("select * from user") List<User> getUsers();
-
需要在核心配置文件中绑定接口
-
-
<mappers><mapper class="com.kuang.dao.UserMapper"/> </mappers>
-
测试
-
本质:反射机制实现
底层:动态代理
Mybatis执行流程
8.3 注解CURD
//方法存在多个参数,所有的参数前面必须加上@Param("id")注解
@Delete("delete from user where id = ${uid}")
int deleteUser(@Param("uid") int id);
关于@Param( )注解
-
基本类型的参数或者String类型,需要加上
-
引用类型不需要加
-
如果只有一个基本类型的话,可以忽略,但是建议大家都加上
-
我们在SQL中引用的就是我们这里的@Param()中设定的属性名
#{} 和 ${}
${param}传递的参数会被当成sql语句中的一部分,比如传递表名,字段名例子:(传入值为id)order by ${param} 则解析成的sql为:order by id#{parm}传入的数据都当成一个字符串,会对自动传入的数据加一个双引号例子:(传入值为id)select * from table where name = #{param}则解析成的sql为:select * from table where name = "id"为了安全,能用#的地方就用#方式传参,这样可以有效的防止sql注入攻击
package com.kuang.mapper;import com.kuang.pojo.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;import java.util.List;
import java.util.Map;public interface UserMapper {@Select("select * from user")public List<User> getUserList();@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{pwd})")public int addUser(User user);@Insert("insert into user(id,name,pwd) values(#{Id},#{Name},#{Pwd})")public int addUser02(Map<String,Object>map);@Delete("delete from user where id=#{id}")public int deleteUserById(int id);@Update("update user set name=#{name},pwd=#{pwd} where id=#{id}")public int updateUser(User user);@Select("select * from user where id=#{id}")public User getUserById(int id);
}
9、Lombok
Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。
使用步骤:
-
在IDEA中安装Lombok插件
-
在项目中导入lombok的jar包
@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder @SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @val
@Data @AllArgsConstructor @NoArgsConstructor public class User {private int id;private String name;private String password; }
Lombok本身是一个优秀的Java代码库,它采用了一种取巧的语法糖,简化了Java的编码,为Java代码的精简提供了一种方式,但在使用此代码库时,需要了解到Lombok并非一个标准的Java库。使用Lombok,会增加团队的技术债务,降低代码的可读性,增大代码的耦合度和调式难度。虽然在一定程度上Lombok减少了样板代码的书写,但也带来了一些未知的风险。
10、多对一处理
多个学生一个老师;
package com.kuang.mapper;import com.kuang.pojo.Student;public interface StudentMapper {public Student getStudent(int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.mapper.StudentMapper"><select id="getStudent" parameterType="int" resultMap="StudentAndTeacher">select * from student where id=#{id}</select><resultMap id="StudentAndTeacher" type="Student"><result property="id" column="id"/><result property="name" column="name"/><association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/></resultMap><select id="getTeacher" parameterType="int" resultType="Teacher">select * from teacher where id=#{tid}</select>
</mapper>
package com.kuang;import com.kuang.mapper.StudentMapper;
import com.kuang.mapper.TeacherMapper;
import com.kuang.pojo.Student;
import com.kuang.pojo.Teacher;
import com.kuang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class MyTest {public static void main(String[] args) {SqlSession sqlSession= MybatisUtils.getSqlSession();StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);Student student=mapper.getStudent(1);System.out.println(student);}
}
Association关联的结果查询
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.kuang.mapper.StudentMapper"><select id="getStudent" parameterType="int" resultMap="StudentAndTeacher">select * from student where id=#{id}</select><resultMap id="StudentAndTeacher" type="Student"><result property="id" column="id"/><result property="name" column="name"/><association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/></resultMap><select id="getTeacher" parameterType="int" resultType="Teacher">select * from teacher where id=#{tid}</select><select id="getStudent02" parameterType="int" resultMap="StudentTeacher2">select s.id sid,s.name sname,t.id tid,t.name tname from student s,teacher t where s.tid=t.id and s.id=#{id};</select><resultMap id="StudentTeacher2" type="Student"><result property="id" column="sid"/><result property="name" column="sname"/><association property="teacher" javaType="Teacher"><result property="id" column="tid"/><result property="name" column="tname"/></association></resultMap>
</mapper>
回顾Mysql多对一查询方式:
-
子查询 (按照查询嵌套)
-
联表查询 (按照结果嵌套)
11、一对多处理
一个老师多个学生;
对于老师而言,就是一对多的关系;
<!--按结果嵌套查询-->
<select id="getTeacher" resultMap="StudentTeacher">SELECT s.id sid, s.name sname,t.name tname,t.id tid FROM student s, teacher tWHERE s.tid = t.id AND tid = #{tid}
</select>
<resultMap id="StudentTeacher" type="Teacher"><result property="id" column="tid"/><result property="name" column="tname"/><!--复杂的属性,我们需要单独处理 对象:association 集合:collectionjavaType=""指定属性的类型!集合中的泛型信息,我们使用ofType获取--><collection property="students" ofType="Student"><result property="id" column="sid"/><result property="name" column="sname"/><result property="tid" column="tid"/></collection>
</resultMap>
这个不用写javaType, 因为查询出来本身就是一条一条的信息
在查询嵌套处理时,可以将column 看作是传入参数
查询嵌套处理
<select id="getTeacher03" resultMap="TeacherStudent02">select * from Teacher where Id=#{uid1};</select><resultMap id="TeacherStudent02" type="Teacher02"><collection property="student02List" javaType="ArrayList" ofType="Student02" column="Id" select="getStudentByTid"/></resultMap><select id="getStudentByTid" resultType="Student02">select * from Student where Tid=#{tid};</select>
小结
-
关联 – association 【多对一】
-
集合 – collection 【一对多】
-
javaType & ofType
-
JavaType用来指定实体类中的类型
-
ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
-
注意点:
-
保证SQL的可读性,尽量保证通俗易懂
-
注意一对多和多对一,属性名和字段的问题
-
如果问题不好排查错误,可以使用日志,建议使用Log4j
面试高频
-
Mysql引擎
-
InnoDB底层原理
-
索引
-
索引优化
12、动态SQL
什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句
所谓的动态SQL,本质上还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
IF语句
where语句
前面几个例子已经方便地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。
<select id="findActiveBlogLike"resultType="Blog">SELECT * FROM BLOGWHERE<if test="state != null">state = #{state}</if><if test="title != null">AND title like #{title}</if><if test="author != null and author.name != null">AND author_name like #{author.name}</if>
</select>
如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:
SELECT * FROM BLOG
WHERE
这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。
但是会导致一些问题,比如第一个条件不成立,只有第二个条件成立,这个时候就变成了 SELECT * FROM BLOG WHERE AND title like ‘someTitle’
使用where标签解决这个问题
MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:
<select id="findActiveBlogLike"resultType="Blog">SELECT * FROM BLOG<where><if test="state != null">state = #{state}</if><if test="title != null">AND title like #{title}</if><if test="author != null and author.name != null">AND author_name like #{author.name}</if></where>
</select>
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
choose语句
<select id="findActiveBlogLike"resultType="Blog">SELECT * FROM BLOG WHERE state = ‘ACTIVE’<choose><when test="title != null">AND title like #{title}</when><when test="author != null and author.name != null">AND author_name like #{author.name}</when><otherwise>AND featured = 1</otherwise></choose>
</select>
最好在choose里面加上and防止出错
set语句
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary">update Author<set><if test="username != null">username=#{username},</if><if test="password != null">password=#{password},</if><if test="email != null">email=#{email},</if><if test="bio != null">bio=#{bio}</if></set>where id=#{id}
</update>
这个例子中,set 元素会动态地在行首插入 SET 关键字,
并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
SQL片段
有的时候,我们可能会将一些功能的部分抽取出来,方便复用!
有的时候,我们可能会将一些功能的部分抽取出来,方便服用!
-
使用SQL标签抽取公共部分可
<sql id="if-title-author"><if test="title!=null">title = #{title}</if><if test="author!=null">and author = #{author}</if> </sql>
-
在需要使用的地方使用Include标签引用即可
<select id="queryBlogIF" parameterType="map" resultType="blog">select * from blog<where><include refid="if-title-author"></include></where> </select>
注意事项:
-
最好基于单标来定义SQL片段
-
不要存在where标签
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了
建议:
-
先在Mysql中写出完整的SQL,再对应的去修改成我们的动态SQL实现通用即可
foreach
说白了就是遍历集合用的,open相当于开头,separator 相当于分隔符 close相当于结尾。
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:
<select id="selectPostIn" resultType="domain.blog.Post">SELECT *FROM POST PWHERE ID in<foreach item="item" index="index" collection="list"open="(" separator="," close=")">#{item}</foreach>
</select>
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
至此,我们已经完成了与 XML 配置及映射文件相关的讨论。下一章将详细探讨 Java API,以便你能充分利用已经创建的映射配置。
13、缓存
13.1 简介
查询 : 连接数据库,耗资源
一次查询的结果,给他暂存一个可以直接取到的地方 –> 内存:缓存
我们再次查询的相同数据的时候,直接走缓存,不走数据库了
-
什么是缓存[Cache]?
-
存在内存中的临时数据
-
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
-
-
为什么使用缓存?
-
减少和数据库的交互次数,减少系统开销,提高系统效率
-
-
什么样的数据可以使用缓存?
-
经常查询并且不经常改变的数据 【可以使用缓存】
-
13.3 一级缓存
-
一级缓存也叫本地缓存:SqlSession
-
与数据库同一次会话期间查询到的数据会放在本地缓存中
-
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
-
测试代码
@Testpublic void test2(){SqlSession sqlSession=MybatisUtil.getSqlSession();BlogMapper mapper=sqlSession.getMapper(BlogMapper.class);Blog blog1=mapper.queryBlogById("2587f90269714b6aa78aaeb2a8a1b492");Blog blog2=mapper.queryBlogById("2587f90269714b6aa78aaeb2a8a1b492");System.out.println(blog1);System.out.println(blog2);System.out.println(blog1==blog2);}
测试结果
这是一个sqlsession过程
缓存失效的情况:
-
查询不同的东西
-
增删改操作,可能会改变原来的数据,所以必定会刷新缓存
-
查询不同的Mapper.xml
-
手动清理缓存
sqlSession.clearCache();
小结:一级缓存默认是开启的,只在一次Sqlsession中有效,也就是拿到链接到关闭链接这个区间段 ,一级缓存相当于一个map
13.4 二级缓存
-
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间,对应一个二级缓存
-
工作机制
-
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
-
如果会话关闭了,这个会员对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
-
新的会话查询信息,就可以从二级缓存中获取内容
-
不同的mapper查询出的数据会放在自己对应的缓存(map)中
-
一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
-
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
-
为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存。
步骤:
第一在配置文件中
<!--显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
第二在xml文件中
<!--在当前Mapper.xml中使用二级缓存-->
<cacheeviction="FIFO"flushInterval="60000"size="512"readOnly="true"/>
测试:
未开启二级缓存的时候
只有在一级缓存执行结束之后,才会将信息缓存到二级缓存中,
@Testpublic void test3(){SqlSession sqlSession=MybatisUtil.getSqlSession();BlogMapper mapper=sqlSession.getMapper(BlogMapper.class);Blog blog=mapper.queryBlogById("2587f90269714b6aa78aaeb2a8a1b492");System.out.println(blog);sqlSession.close();SqlSession sqlSession1=MybatisUtil.getSqlSession();BlogMapper mapper1=sqlSession1.getMapper(BlogMapper.class);Blog blog1=mapper1.queryBlogById("2587f90269714b6aa78aaeb2a8a1b492");System.out.println(blog1);sqlSession1.close();}
小结:
-
只要开启了二级缓存,在同一个Mapper下就有效
-
所有的数据都会放在一级缓存中
-
只有当前会话提交,或者关闭的时候,才会提交到二级缓存中
13.5 缓存原理
13.6 自定义缓存-ehcache
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
导包
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.2.1</version>
</dependency>
关于动态sql
where解决and问题 set解决,逗号问题
模糊查询
模糊查询这么写?
-
Java代码执行的时候,传递通配符% %
List<User> userList = mapper.getUserLike("%李%");
-
1
-
-
在sql拼接中使用通配符
select * from user where name like "%"#{value}"%"
-
1
-