最近在b站关注了一个神仙up主,狂神说Java,up主讲课的风格我还是很喜欢的,听着不困,花两天把mybatis学了一下,跟着视频记录了点笔记
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。JDBC就是一种持久化机制。文件IO也是一种持久化机制。什么是持久层是完成持久化工作的代码块
dao层 【DAO (Data Access Object) 数据访问对象】大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。参考:https://mybatis.org/mybatis-3/zh/getting-started.html 1、搭建数据库环境
2、使用Maven导入Mybatis相关jar包 我碰到了无法导入的情况,根据这篇解决了:maven换源方法
<!-- 导入依赖 --> <dependencies> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency> <!-- mybatis --> <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> <scope>test</scope> </dependency> </dependencies>3、创建一个模块
【先创建父工程,删除其src文件夹,配置好pom.xml后,创建子模块,这样子模块的依赖就直接用父工程的就行了】
编写Mybatis核心配置文件mybatis-config.xml(连接数据库)
<?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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--& 在xml里面加 amp; 进行转义 --> <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>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/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </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=xxx2、在核心配置文件中引入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 映射文件了
注意点:
接口和其Mapper配置文件必须同名接口和其Mapper配置文件必须在同包下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为主键 --> <id column="id" property="id"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <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<String,Integer>对象就ok了
1、添加接口
//选择全部用户实现分页 List<User> getUserByLimit(Map<String,Integer> map);2、修改Mapper
<!-- 结果集映射,将数据库映射为pojo --> <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内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
SLF4JApache Commons LoggingLog4j 2Log4j 【掌握】JDK loggingSTDOUT_LOGGING【掌握】具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。
<settings> <setting name="logImpl" value="LOG4J"/> </settings>配置文件: https://www.cnblogs.com/wangzhuxing/p/7753420.html
什么是LOG4J?
Log4j是Apache的一个开源项目通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件…我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。简单实用:
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=DEBUG3、setting设置日志实现
<settings> <!--setting name="logImpl" value="STDOUT_LOGGING"/--> <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","098@qq.com")); session.close(); } @Test public void updateUser(){ SqlSession session = MybatisUtils.getSqlSession(); //本质上利用了jvm的动态代理机制 UserMapper mapper = session.getMapper(UserMapper.class); mapper.updateUser(new User(8,"贺老板","000","098@qq.com")); 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文件
<?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.kuang.mapper.StudentMapper"> <!-- 需求:获取所有学生及对应老师的信息 思路: 1. 获取所有学生的信息 2. 根据获取的学生信息的老师ID->获取该老师的信息 3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询? 1. 做一个结果集映射:StudentTeacher 2. StudentTeacher结果集的类型为 Student 3. 学生中老师的属性为teacher,对应数据库中为tid。 多个 [1,...)学生关联一个老师=> 一对一,一对多 4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询 --> <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> </mapper>1、接口方法编写
public List<Student> getStudents2();2、编写对应的mapper文件
<!-- 按查询结果嵌套处理 思路: 1. 直接查询出结果,进行结果集的映射 --> <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"/> <!--关联对象property 关联对象在Student实体类中的属性--> <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都是用来指定对象类型的
JavaType是用来指定pojo中属性的类型ofType指定的是映射到list集合属性中pojo的类型。动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.
根据作者名字和博客名字来查询博客,如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
1、写接口类
List<Blog> queryBlogIf(Map map);2、用if标签写xml
<!--需求1: 根据作者名字和博客名字来查询博客! 如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询 select * from blog where title = #{title} and author = #{author} --> <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系统中默认定义了两级缓存:
一级缓存和二级缓存默认情况下,只有一级缓存开启。SqlSession级别的缓存,也称为本地缓存二级缓存需要手动开启和配置,他是基于namespace级别的缓存。为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存