本篇博客整理了我两天刷完mybatis几乎所有的知识点,和一些折磨过我的错,我相信大家也都一定踩过,下面让我们一起来往下看。
学什么之前一定要搞清楚这东西是什么,可以做什么
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护。sql和代码的分离,提高了可维护性。
- 提供xml标签,支持编写动态sql
- 提供映射标签,支持对象与数据库的orm字段关系映射
思路:搭建数据库->导入mybatis->编写代码->测试
1.搭建数据库
2.导入依赖
org.mybatis
mybatis
3.5.7
mysql
mysql-connector-java
8.0.18
junit
junit
4.13
test
3.编写mybatis核心配置文件(myabtis-config.xml)
4.连接数据库
5.建一个mybatis工具类—MybatisUtils(获取sqlSessionFactory)
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;//提升SqlSessionFactory作用域
static {
//获取sqlSessionFactory对象
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了sqlSessionFactory对象,所以我们要获得sqlSession的实例
public static SqlSession getSqlSession(){
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}
6.编写代码
1.实体类
public class user {
private int id;
private String name;
private int age;
public user() {
}
public user(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "user{" +
"id=" + id +
", name='" + name + ''' +
", age=" + age +
'}';
}
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2.编写dao ->UserMapper
public interface UserMapper {
List getUserList();
}
2.1对应的映射UserMapper.xml,这里的映射我不知道说的标准不标准,如有错,麻烦点评,谢谢各位老哥
7.测试
public class UserDaoTest {
@Test
public void test(){
//1.获得sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2.执行sql
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
List userList = usermapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
8.测试成功
需要注意:
UserMapper.xml配置完,mybatis核心配置里面关联dao接口记得加上
4.mybatis增删改查实现myabtis的增删改查比较容易,只需要动接口和接口对应的配置文件里面写对应的sql即可,测试唯一需要注意的点就是增删改需要提交事务,养成良好习惯,记得关闭sqlSession。sqlSession.close
4.1根据id查询用户
4.2增加一个用户
需要注意:增删改必须得提交事务!
4.3修改用户
4.4删除一个用户
原本是打算很快速的溜一遍mybatis,结果层层出错,调试很久很久,我简单把我遇到的问题跟大家提一嘴
- mybatis-config.xml这个核心配置文件里面的mapper,class和resource的问题,如果你用class,你必须要保证你的接口和对应的接口配置都在同一包下,还要保证名字相同才可以用class,如果你用resource的话,记得是磁盘路径那种,以/做间隔,我第一次写博客,词汇量还不太够,打扰打扰
- 也是mybatis-config.xml这里面,url里面的各种配置一定要注意,就像我觉得简单没问题,就从网上复印的别人的,到最后这个错误我找了1个小时,才发现那人的配置UTF-8,他写成了UFT-8,我就很无语
- 数据库如果是8版本以上的,用mybatis要注意时区问题,为了追求完美driver驱动也应该加个cj,不加也没什么。只会有一行红线罢了,但不影响运行
- maven的静态资源过滤问题,代码网上很多,最好给他加上,如果不加上,每次运行生成的target文件里面dao就会没有接口的配置文件,每次都需要手动添加很麻烦
- 最后,一定要有耐心,我就因为一个错2-3个小时找不出来,堕落了几天,呜呜心态崩了直接
以后的项目大起来,字段会非常非常的多,我们正常查询或者修改,是有多少字段就会new多少字段出来,但是你用了map就可以很方便,HashMap
Mybatis中有两种类型的事务管理器:
- JDBC:这个配置是直接使用JDBC的提交和事务回滚,它依赖于从数据源得到的连接来管理作用域
- MANAGED:它几乎没做什么,它从来不提交或回滚一个连接,而是让容器管理事务的整个生命周期。默认情况它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认关闭。
Mybatis的默认管理器是JDBC,连接池:POOLED
properties
在db.properties里面url不用转义字符,不然会报错,还有一点username和password要清晰,就算有个空格都不行,都会报错,要细心
这种情况适用于实体类比较少的时候,好处是可以diy,就是自定义别名
第二种情况适用于实体类比较多的时候,采用的是包扫描,resultType默认的是实体类的小写
第二种方式也可以用注解设置别名@Alias(“XXX”)
作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
1.一旦创建了sqlSessionFactory,就不在需要它了
SqlSessionFactory
- 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- SqlSessionFactory 的最佳作用域是应用作用域
- 最简单的就是使用单例模式或者静态单例模式。(可以保证全局只有一个变量
SqlSession
- 连接到我们连接池的一个请求
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 所以我们一般会把它用在一个方法里面,用完立马关闭,否则资源被占用
结果集映射:
属性名和字段名不一致问题:resultmap
column:数据库中的字段
property:实体类中的属性
resultMap 元素是 MyBatis 中最重要最强大的元素
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
10.日志日志:排错,如果一个数据库出现异常,我们需要排错,日志就是一个很好的方式
-
SLF4J
-
LOG4J【掌握
-
LOG4J2
-
JDK_LOGGING
-
COMMONS_LOGGING
-
STDOUT_LOGGIN【掌握
-
NO_LOGGING
什么是log4j?
Log4j是Apache的一个开源项目
通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
我们也可以控制每一条日志的输出格式
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要
log4j的使用:
1.导入依赖修改应用的代码。
log4j log4j 1.2.17
2.设置setting
``` 3.创建log4j.properties ```xml #将等级为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/rzp.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
4.运行测试
简单使用:
- 在要使用log4j类中,导入import org.apache.log4j.Logger;
- 日志对象,参数为当前对象的.class
static Logger logger = Logger.getLogger(MyTest.class);
@Test
public void log4jTest(){
logger.info("info:进入了log4jTest");
logger.debug("debug:进入了log4jTest");
logger.error("error:进入了log4jTest");
}
11.分页
思考:为什么要分页?
减少数据的处理量
使用Limit分页
SELECT * from mybatis.user limit startIndex,pageSize; SELECt * from mybatis.user limit 0,2;
使用mybatis分页,核心SQL
1.接口
ListgetUserByLimit(Map map);
2.Mapper.xml
12.RowBoundsselect * from mybatis.user limit #{startIndex},#{pageSize} ``` 测试: ```java @Test public void getUserByLimit(){ SqlSession sqlSession = MyBatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMapmap = new HashMap (); map.put("startIndex",0); map.put("pageSize",2); List userList = mapper.getUserByLimit(map); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
不在使用Sql实现分页
1.接口
ListgetUserByRowBounds();//分页2
2.mapper.xml
3.测试
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
RowBounds rowBounds = new RowBounds(1, 2);
//基于java代码层面实现分页
List userList = sqlSession.selectList("com.zhang.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
使用注解开发
1.接口
public interface UserMapper {
@Select("select * from mybatis.user ")
List getUser();
}
2.mybatis核心配置—mappers(使用注解接口对应的配置就可以省略,mybatis绑定的就是我们的接口而不是接口对应的配置)
3.测试
key看到实体类字段和数据库字段不一致的情况下,使用mybatis注解的方式,实体类userAge属性输出出现问题,所以mybatis使用注解简单的可以处理,较为复杂的情况官方还是推荐使用配置的方式来实现。
我们可以在工具类创建的时候设置为自动提交事务,为true即可
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
基本类型,多个参数的情况下,必须使用@Param注解,作用是给参数命名,也变得更加规范#{}这里所引用的就是@Param里面设置的命名
这里提一下#{}和${}问题
- 一般我们使用#{},不使用 , 因 为 {},因为 ,因为{}会引起sql注入的问题
- 我们不使用@param注解时候,必须使用#{},使用${}会报错
1.接口
public interface UserMapper {
@Select("select * from mybatis.user ")
List getUser();
@Select("select * from mybatis.user where id = #{id}")
User getUserById(@Param("id") int id);
@Insert("insert into mybatis.user(id,name,age) value(#{id},#{name},#{userAge})")
int addUser(User user);
@Update("update mybatis.user set name=#{name},age=#{userAge} where id =#{id}")
int updateUser(User user);
@Delete("delete from mybatis.user where id = #{uid}")
int deleteUser(@Param("uid") int id);
}
2.实现(中间步骤和之前一样,没有变动
public class MyTest {
@Test
public void test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// List userList = mapper.getUser();
// for (User user : userList) {
// System.out.println(user);
// }
// User userById = mapper.getUserById(2);
// System.out.println(userById);
// mapper.addUser(new User(7,"hello",12));
// mapper.updateUser(new User(7,"wowo",4));
mapper.deleteUser(7);
}
}
Lombok
lombok使用:
- 安装lombok插件
- 导入lombok依赖
- 实体类上加注解
lombok生成了无参,get,set,toString,hashcode,equals
第一步:搭建多对一环境
数据库:
2.编写代码
3.测试
环境搭建成功
接口
//查询所有学生的信息以及老师的信息
public List getStudent();
接口对应的mapper
最后测试即可
我对多对一的理解:
按照查询嵌套处理:多对一这个一的属性在这代码里是老师,Student的实体类属性把老师这个对象引入进来替代student属性里tid这个属性,在运用mapper.xml文件里面对student进行结果集映射resultMap,之后就可以对应的补充实体类属性和数据库属性的对应添加,老师这个引用过来的对象也可以顺利成章的放进来了,从而完成真正的多对一查询。
按照结果嵌套处理
场景:一个老师对应多个学生
@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 students;
}
个人分析:一个老师对应多个学生,就在老师的代码操作,因为是多个学生,所以这里都用集合存储list
接口
public interface TeacherMapper {
//获取指定老师下的所有学生
Teacher getTeacher(@Param("tid") int id);
}
xml
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}
测试
public class MyTest {
@Test
public void test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
}
}
注意事项!!
- 我个人认为做这个一对多,多对一 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} 写这种sql的时候一定要细心,有一个逗号很不明显的错误都会困扰你很久很久,写sql一句一定要认真,争取写一遍知道自己没有错,在去做别的
什么是动态Sql:动态SQL就是指根据不同的条件可以生成不同的sql语句
缓存什么是缓存?
- 存在内存中的临时数据
- 用户经常查的数据放在缓存中(内存),用户去查询就不用从磁盘上查询了,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
为什么使用缓存?
减少和数据库之间的交互次数,减少系统开销,提高系统效率
什么样的数据能使用缓存?
经常查询并且不经常改变的数据
mybatis缓存- Mybatis包含了一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存。缓存可以极大的提升查询效率。
- mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
-
- 一级缓存:也称为本地缓存,是sqlsession级别的缓存,默认情况下,一级缓存会开启
- 二级缓存需要手动配置和开启,是基于namespace级别的缓存
- 为了提高扩展性,mybatis还自定义了cache缓存接口。我们可以通过cache来自定义二级缓存
mybatis默认的缓存策略 LRU,意思就是最长时间不被使用的对象会被移除。
mybatis先进先出原则,按对象缓存的顺序来移除他们,可以理解为队列
缓存失效的情况:
-
增删改,可能会改变原来的数据,必定会刷新缓存
-
手动清理缓存 sqlSession.clearCache();
-
查询不同的mapper.xml
-
查询不同的东西
一级缓存是默认开启的,只在一次sqlSession中有效,也就是拿到连接到关闭连接这个区间段。
在当前xml中使用二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存,基于namespace级别的缓存,一个名称空间,对应一个二级缓存
视频课听完感觉mybatis中的缓存没什么,就写的比较粗糙。



