最近在b站关注了一个神仙up主,狂神说Java,up主讲课的风格我还是很喜欢的,听着不困,花两天把mybatis学了一下,跟着视频记录了点笔记
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
什么是持久层是完成持久化工作的代码块
参考:https://mybatis.org/mybatis-3/zh/getting-started.html
1、搭建数据库环境
2、使用Maven导入Mybatis相关jar包
我碰到了无法导入的情况,根据这篇解决了:maven换源方法
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.19version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
3、创建一个模块
【先创建父工程,删除其src文件夹,配置好pom.xml后,创建子模块,这样子模块的依赖就直接用父工程的就行了】
编写Mybatis核心配置文件mybatis-config.xml(连接数据库)
<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/book?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="xxxxxx"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
mappers>
configuration>
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//1、使用Mybatis来获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//从 SqlSessionFactory获得 SqlSession 的实例。
//你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
public interface UserDao {
public List<User> getUserList();
}
<mapper namespace="com.lqr.dao.UserDao">
<select id="getUserList" resultType="com.lqr.pojo.User">
select * from t_user
select>
mapper>
1、测试代码
public class UserDaoTest {
@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);
}
sqlSession.close();
}
}
1、【重要】配置文件没有注册
2、绑定接口错误(namespace中的包名要和接口中的包名一致)
3、方法名不对
4、返回类型不对
5、【重要】Maven静态资源过滤问题,在pom文件里插入下面的代码,并且刷新maven
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
6、Intellij idea 报错:Error : java 不支持发行版本5
项目编译配置使用的Java版本不对,需要检查一下项目及环境使用的Java编译版本配置。
https://blog.csdn.net/qq_22076345/article/details/82392236
1、编写UserMapper接口中方法
2、编写UserMapper.xml中对应的sql语句
3、测试
测试的时候要注意:增删改要提交事务
sqlSession.commit();
假设我们的实体类中,或者数据库中的表,字段,参数过多,可以考虑使用Map传参,直接在sql中取出key即可
int addUser2(Map<String,Object> map);
4、模糊查询
(1)java代码执行的时候传递通配符;
List<User> userList = mapper.getUserLike("l");
(2)在xml文件sql拼接中使用通配符,【把格式写死,预防sql注入!】
<select id="getUserLike" parameterType="String" resultType="com.lqr.pojo.User">
select * from t_user where username like "%"#{value}"%"
select>
核心配置文件——mybatis-config.xml。其中能配置的内容如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
1、配置文件编写 db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username=root
password=xxx
2、在核心配置文件中引入properties
<properties resource="db.properties">properties>
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。 例如:
(1)typeAlias
<typeAliases>
<typeAlias type="com.lqr.pojo.User" alias="user"/>
typeAliases>
(2)也可以指定一个包名,MyBatis 会在包名下面搜索每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
<typeAliases>
<typeAlias type="com.lqr.pojo.User" alias="user"/>
<package name="com.lqr.pojo"/>
typeAliases>
(3)还可以直接使用注解
@Alias("user")
public class User{
...
}
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
<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>
其中比较重要的:
mapUnderscoreToCamelCase
是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn(last_name —— lastName)。可设置为true | false
logImpl
指定 MyBatis 所用日志的具体实现,未指定时将自动查找。可设置为SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
MapperRegister:注册绑定我们的Mapper文件。
这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了
注意点:
1、方式1,resource标签,使用相对于类路径的资源引用
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
mappers>
2、方式2【推荐】,class标签,使用映射器接口实现类的完全限定类名
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
mappers>
3、方式3
<mappers>
<package name="org.mybatis.builder"/>
mappers>
不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。
下图来自:狂神说公众号
要解决的问题:属性名和字段名不一致(pojo类中字段名和数据库中字段名不一致)
比如在mapper.xml中写下
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
select>
这句select * from user where id = #{id}
其实可以看做在执行select id,name,pwd from user where id = #{id}
。
mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值
假设我们pojo中密码对应是“pwd”,而数据库中对应“password”,所以查不到pwd对应的结果,所以在这里需要用到resultMap完成一个由pwd到password的映射。
<resultMap id="UserMap" type="User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="password" property="pwd"/>
resultMap>
为什么要分页
查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。
MySQL中LIMIT语法
SELECT * FROM table LIMIT stratIndex,pageSize
SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15
#为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
#如果只给定一个参数,它表示返回最大的记录行数目:
SELECT * FROM table LIMIT 5; //检索前 5 个记录行
【那么如何在Mybatis中实现分页?】
我们可以用Map传参完成分页,假设参数为stratIndex,pageSize,因此建一个Map
1、添加接口
//选择全部用户实现分页
List<User> getUserByLimit(Map<String,Integer> map);
2、修改Mapper
<resultMap id="UserMap" type="User">
<result column="password" property="pwd"/>
resultMap>
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from t_user limit #{startIndex}, #{pageSize}
select>
3、测试
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.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);
}
sqlSession.close();
}
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
配置文件:
https://www.cnblogs.com/wangzhuxing/p/7753420.html
什么是LOG4J?
简单实用:
1、导入log4j的包
2、配置文件编写
#将等级为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/lqr.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.sql.PreparedStatement=DEBUG
3、setting设置日志实现
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
4、在程序中使用Log4j进行输出!
static Logger logger = Logger.getLogger(UserMapperTest.class);
@Test
public void testLOG4J(){
logger.info("info:进入selectUser方法");
logger.debug("debug:进入selectUser方法");
logger.error("error: 进入selectUser方法");
}
在MybatisUtils中设置:sqlSessionFactory.openSession(true);
public class MybatisUtils {
//提升作用域
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//1、使用Mybatis来获取SqlSessionFactory对象(来自官方文档)
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//2、通过SqlSessionFactor 获得 SqlSession 的实例。
//SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。通过 SqlSession 实例来直接执行已映射的 SQL 语句
public static SqlSession getSqlSession(){
//设置为true就是自动提交事务
return sqlSessionFactory.openSession(true);
}
}
mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。【但是Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建】
1、我们在我们的接口中添加注解
2、注意【在mybatis的核心配置文件中注入】
3、关于@Param()注解:基本类型和String需要加上,引用类型不需要加,如果只有一个基本类型可以忽略(不过建议加上)
<mappers>
<mapper class="com.lqr.dao.UserMapper"/>
mappers>
3、代码
编写接口UserMapper ,在接口方法上添加注解。
这里有一点要注意,@Param中的参数一定要和注解中的参数统一。
public interface UserMapper {
//查询所有结果
@Select("select * from t_user")
List<User> getUserList();
//方法前有多个参数,都在前面加上@Param
@Select("select * from t_user where id = #{id}")
User getUserById(@Param("id") int id);
//插入
@Insert("insert into t_user(id,username,password,email) values(#{id},#{username},#{pwd},#{email})")
int addUser(User user);
//更新
@Update("update t_user set username = #{username} where id = #{id}")
int updateUser(User user);
//删除
@Delete("delete from t_user where id = #{uid}")
int deleteUser(@Param("uid") int id);
}
测试:UserMapperTest.java
public class UserMapperTest {
@Test
public void getUserList(){
SqlSession session = MybatisUtils.getSqlSession();
//本质上利用了jvm的动态代理机制
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getUserList();
for (User user : users){
System.out.println(user);
}
session.close();
}
@Test
public void getUserById(){
SqlSession session = MybatisUtils.getSqlSession();
//本质上利用了jvm的动态代理机制
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
session.close();
}
@Test
public void addUser(){
SqlSession session = MybatisUtils.getSqlSession();
//本质上利用了jvm的动态代理机制
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.addUser(new User(6,"johnson","000","[email protected]"));
session.close();
}
@Test
public void updateUser(){
SqlSession session = MybatisUtils.getSqlSession();
//本质上利用了jvm的动态代理机制
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.updateUser(new User(8,"贺老板","000","[email protected]"));
session.close();
}
@Test
public void deleteUser(){
SqlSession session = MybatisUtils.getSqlSession();
//本质上利用了jvm的动态代理机制
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.deleteUser(8);
session.close();
}
}
第一次理解起来比较晦涩,建议结合子查询和连接查询好理解点。
【student表和teacher表】
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');
SELECT * FROM teacher;
SELECT * FROM student;
1、pojo包下
【在代码中添加注解,这里用到了lombok】
@Data //GET,SET,ToString,有参,无参构造
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
//多个学生可以是同一个老师,即多对一
private Teacher teacher;
}
2、在dao包下编写两个pojo类对应的mapper 接口
3、思考:
按照查询进行嵌套处理就像SQL中的子查询
按照结果进行嵌套处理就像SQL中的联表查询
1、给StudentMapper接口增加方法
//获取所有学生及对应老师的信息
public List<Student> getStudents();
2、编写对应的StudentMapper文件
<mapper namespace="com.kuang.mapper.StudentMapper">
<select id="getStudents" resultMap="StudentTeacher">
select * from student
select>
<resultMap id="StudentTeacher" type="Student">
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
select>
mapper>
1、接口方法编写
public List<Student> getStudents2();
2、编写对应的mapper文件
<select id="getStudents2" resultMap="StudentTeacher2" >
select s.id sid, s.name sname , t.name tname
from student s,teacher t
where s.tid = t.id
select>
<resultMap id="StudentTeacher2" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
association>
resultMap>
1、pojo包下
在代码中添加注释
@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;
}
1、关联-association
2、集合-collection
3、所以association是用于一对一和多对一,而collection是用于一对多的关系
4、JavaType和ofType都是用来指定对象类型的
动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.
根据作者名字和博客名字来查询博客,如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
1、写接口类
List<Blog> queryBlogIf(Map map);
2、用if标签写xml
<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>
where>
select>
1、什么是缓存 [ Cache ]?
2、为什么使用缓存?
3、什么样的数据能使用缓存?
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存: