MyBatis学习笔记
数据持久化
持久化就是将程序的数据在持久状态和瞬时状态转化的过程
Dao层、Service层,Controller层
简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
。sql和代码的分离,提高了可维护性。
。提供映射标签,支持对象与数据库的orm字段关系映射
。提供对象关系映射标签,支持对象关系组建维护
。提供xml标签,支持编写动态sq|。
关于数据库引擎
https://www.jianshu.com/p/4bb9f78b4f6d
InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键,InnoDB给MySQL提供了具有提交、回滚和崩溃恢复能力的事物安全(ACID兼容)存储引擎
InnoDB是为处理巨大数据量的最大性能设计。
InnoDB存储引擎完全与MySQL服务器整合,InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池
如果要提供提交、回滚、崩溃恢复能力的事物安全(ACID兼容)能力,并要求实现并发控制,InnoDB是一个好的选择
如果数据表主要用来插入和查询记录,则MyISAM引擎能提供较高的处理效率
如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,可以选择将数据保存在内存中的Memory引擎,MySQL中使用该引擎作为临时表,存放查询的中间结果
如果只有INSERT和SELECT操作,可以选择Archive,Archive支持高并发的插入操作,但是本身不是事务安全的。Archive非常适合存储归档数据,如记录日志信息可以使用Archive
使用哪一种引擎需要灵活选择,一个数据库中多个表可以使用不同引擎以满足各种性能和实际需求,使用合适的存储引擎,将会提高整个数据库的性能
1.新建一个普通的maven项目
2.删除src目录
3.导入maven依赖
<dependencies>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
JUnit是一个Java语言的单元测试框架。
junit的作用:
通常我们写完代码想要测试这段代码的正确性,那么必须新建一个类,然后创建一个 main() 方法,然后编写测试代码。如果需要测试的代码很多呢?那么要么就会建很多main() 方法来测试,要么将其全部写在一个 main() 方法里面。这也会大大的增加测试的复杂度,降低程序员的测试积极性。而 Junit 能很好的解决这个问题,简化单元测试,写一点测一点,在编写以后的代码中如果发现问题可以较快的追踪到问题的原因,减小回归错误的纠错难度。
新建Module
在resource下新建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.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/zzt?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
</configuration>
编写工具类:
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用Mybatis第一步:获取sqlsessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(); //创建一个能执行sql的对象
}
}
编写实体类User。
编写Dao接口UserDao
public interface UserDao {
List<User>getUserList();
}
接口实现类UserMapper.xml(由原来的UserDaolmpl转变为一个Mapper配置文件)
<?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">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.lmj.dao.UserDao">
<select id="getUserList" resultType="com.lmj.pojo.User">
select * from user
</select>
</mapper>
junit测试:
maven由于他的约定大于配置,我们之后可以能遇到我们写的配置文件,无法被导出或者生效的问题。
在pom.xml配置文件中加入
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties
**/ *.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties
**/ *.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
进行测试:
public class UserDaoTest {
@Test
public void test(){
//第一步:获得Sqlsession对象
SqlSession sqlSession= MybatisUtils.getSqlSession();
//方式一:getMapper
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList=userDao.getUserList();
for (User user : userList) {
System.out.println(user);
}
//关闭Sqlsession
sqlSession.close();
}
}
namespace中的包名要和Dao/Mapper接口的包名一致
增删改操作需要提交事务 sqlSession.commit()
id为namespace下方法的名字
resultype:sql语句执行的返回值
parameterType :参数类型
1.编写接口
//获取全部用户
List<User>getUserList();
//根据id查询
User getUserById(int id);
2.编写对应mapper中的sql语句
<mapper namespace="com.lmj.dao.UserMapper">
<select id="getUserList" resultType="com.lmj.pojo.User">
select * from user
</select>
<!--insert没有返回-->
<insert id="addUser" parameterType="com.lmj.pojo.User" >
insert into user(id,username, password) values (#{
id},#{
username},#{
password});
</insert>
</mapper>
3.测试
@Test
public void addUser(){
SqlSession sqlSession= MybatisUtils.getSqlSession();
//获得接口
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.addUser(new User(12,"testpeople","123"));
if(res>0) {
System.out.println("插入成功");
}
//提交事务
sqlSession.commit();
sqlSession.close();
}
假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map
1.Map传递参数,直接在sql中取出key即可
2.对象传递参数,直接在sq|中取对象的属性即可
3.只有一个基本类型参数的情况下,可以直接在sq|中取到
4.多个参数用Map,或者注解!
<!--传入map其中user_id为map的键值对-->
<insert id="addUser2" parameterType="map" >
insert into user(id,username, password) values (#{
user_id},#{
user_name},#{
user_password});
</insert>
测试:
public void addUser2(){
SqlSession sqlSession=MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("user_id",20);
map.put("user_name","lmj");
map.put("user_password","531123");
int res=mapper.addUser2(map);
if(res>0) {
System.out.println("插入成功2");
}
//提交事务
sqlSession.commit(); //在工具类中设置sqlSessionFactory.openSession(true)可以自动提交
sqlSession.close();
}
3.数据源
有三种内建的数据源类型也就是type=[UNPOOLED | POOLED | JNDI]") :
UNPOOLED-这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单应用程序来说,是一个很好的选择。
POOLED-这种数据源的实现利用“池”的概念将JDBC连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。这是一种使得并发Web应用快速响应请求的流行处理方式。
JNDI -这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。这种数据源配置只需要两个属性:
mybatis的默认事务管理器是JDBC , 连接池POOLED
属性(properties)
引入外部配置文件db.properties(优先使用)
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/zzt?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123
在mybatis-config.xml文件中引入
<!--引入外部配置文件-->
<properties resource="db.properties">
<property name="" value=""/>
</properties>
<environments default="development"> <!--default默认环境-->
<environment id="development">
<transactionManager type="JDBC"/> <!--事务管理有两种类型(也就是type="[JDBC|MANAGED]"-->
<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>
4.类型别名
●类型别名是为Java类型设置一个短的名字。
●存在的意义仅在于用来减少类完全限定名的冗余。
<typeAliases>
<typeAlias type="com.lmj2.pojo.User" alias="User"/>
</typeAliases>
也可以指定一个包名,MyBatis会在包名下面搜索需要的Java Bean,比如:
扫描实体类的包,它的默认别名就为这个类的类名,首字母小写!
在实体类上增加 注解别名 @Alias(“user”)
这是MyBatis中极为重要的调整设置,它们会改变MyBatis的运行时行为。
cacheEnabled:全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置tetchrype属性来覆盖该项的开关状态。
●typeHandlers (类型处理器)
●objectFactory (对象工厂)
●plugins插件
。mybatis-generator-core
。mybatis-plus
。通用mapper
MapperRegistry:注册绑定我们的Mapper文件
<mappers>
<!--方式一(推荐): <mapper resource="com/lmj2/dao/UserMapper.xml"/> -->
<!--方式二: <mapper class="com.lmj2.dao.UserMapper"/> -->
<!--方式三: <package name="com.lmj2.dao"/> -->
</mappers>
方式二、方式三注意:
●接口和他的Mapper配置文件必须同名!
●接口和他的Mapper配置文件必须在同一个包下!
生命周期,和作用域,是至关重要的,因为错误的使用会导致非常严重的并发问题
总体流程
SqlSessionFactoryBuilder:
●一旦创建了SqlSessionFactory,就不再需要它了
●局部变量
sqlSessionFactory:
●说白了就是可以想象为:数据库连接池
●SqlSessionFactory 一旦被创建就应该在应用的运行期间一-直存在,没有任何理由丢弃它或重新创建另一个实例。
●因此SqISessionFactory的最佳作用域是应用作用域。
●最简单的就是使用单例模式或者静态单例模式。
SqlSession
●连接到连接池的一一个请求!
●SqISession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
●用完之后需要赶紧关闭,否则资源被占用!
1.直接修改数据库语句
select id username password as pwd from user where id=#{
id}
2.resultMap结果集映射
<resultMap id="UserMap" type="User">
<!-- colum为数据库字段,property为实体类字段,两者字段一致可省略-->
<result column="password" property="pwd" />
</resultMap>
<!--parameterType为参数类型-->
<select id="getUserById" parameterType="int" resultMap="UserMap">
select * from user where id=#{
id}
</select>
结果映射
resultMap 元素是 MyBatis 中最重要最强大的元素
日志工厂:
Mybatis通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
●SLF4J
●LOG4J [掌握]
●LOG4J2
●JDK_ LOGGING
●COMMONS_ LOGGING
●STDOUT_ LOGGING [掌握]
●NO_ LOGGING
在Mybatis中具体使用那个一日志实现,在设置中设定!
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
STDOUT_ LOGGING标准日志输出
什么是Log4j
●Log4j是Apache的-一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
●我们也可以控制每一 条日志的输出格式;
●通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
●通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1.导入log4j包
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2.log4j.properties
### 配置根 ###
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console 和file的定义在下面的代码
log4j.rootLogger = debug,console ,file
### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ###
log4j.logger.org.apache=dubug
log4j.logger.org.mybatis=dubug
#log4j.logger.java.sql.Connection=dubug
log4j.logger.java.sql.Statement=dubug
log4j.logger.java.sql.PreparedStatement=dubug
log4j.logger.java.sql.ResultSet=dubug
### 配置输出到控制台 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = %d{
ABSOLUTE} %5p %c{
1 }:%L - %m%n
### 配置输出到文件 ###
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.File = logs/lmj_log.log
log4j.appender.file.MaxFileSize = 10mb
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{
yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
3.配置log4j
<settings>
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/> -->
<setting name="logImpl" value="LOG4J"/>
</settings>
4.log4j的使用
static Logger logger = Logger.getLogger(UserMapperTest.class);
logger.info("info:进入了testlog4j"); //相当于以前的测试输出信息
logger.debug("debug:进入了testlog4j");
logger.error("error:进入了testlog4j");
使用Limit分页
select * from user limit startIndex , pageSize;
//startIndex 从第几个开始查
//pageSize 每页显示的个数
//limit 4 默认从1到3
//特殊:limit 0 -1 查询全部(mysql的bug现已修复,现在会报错)
使用Mybatis实现分页,核心Sql
1.接口
//分页
List<User> getUserByLimit(Map<String,Integer> map);
2.Mapper.xml
<!--分页-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from user limit #{
startIndex},#{
pageSize}
</select>
3.测试
@Test
public void getUserByLimit(){
SqlSession sqlSession=MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//构造hashmap
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userByLimit = mapper.getUserByLimit(map);
for (User user : userByLimit) {
System.out.println(user);
}
}
RowBounds实现分页
1.接口
//分页2
List<User> getUserByRowBounds();
2.Mapper.xml
<!--分页2-->
<select id="getUserByRowBounds" resultMap="UserMap">
select * from user
</select>
3.测试
@Test
public void getUserByRowBounds(){
SqlSession sqlSession=MybatisUtils.getSqlSession();
//RowBounds实现
RowBounds rowBounds=new RowBounds(1,2);
//通过java代码层面实现分页
List<User> userList=sqlSession.selectList("com.lmj2.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
}
本质:是用反射实现
简单的sql语句可以用注解去写,复杂的可以用配置文件去写
接口代码
public interface UserMapper {
//获取全部用户
@Select("select * from user")
List<User> getUserList();
//根据id查询
@Select("select * from user where id=#{id}")
User getUserById(@Param("id") int id);
}
config.xml配置文件中绑定接口
<!--绑定接口-->
<mappers>
<mapper class="com.lmj.dao.UserMapper"/>
</mappers>
测试代码
@Test
public void test(){
SqlSession sqlSession= MybatisUtils.getSqlSession();
UserMapper mapper=sqlSession.getMapper(UserMapper.class);
List<User> users= mapper.getUserList();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void test2(){
SqlSession sqlSession= MybatisUtils.getSqlSession();
UserMapper mapper=sqlSession.getMapper(UserMapper.class);
User user=mapper.getUserById(3);
System.out.println(user);
}
方便编写实体类的get和set等一些方法
使用步骤:
1.在IDEA中安装Lombok插件
2.在项目中导入lombok的jar包
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
说明:
@Data:无参构造,get、 set.tostring. hashcode, equals
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Getter
<mapper namespace="com.lmj.dao.TeacherMapper">
<!--id为namespace下方法的名字-->
<select id="getTeacher" resultMap="TeacherStudent">
select t.id tid,t.xm tname,s.name sname,s.sex ssex,s.age sage
from teacher t,student s
where t.tid=s.tid
and t.tid=#{
tid};
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="xm" column="tname"/>
<!--复杂的属性,需要单独处理. -->
<collection property="students" ofType="Student">
<result property="name" column="sname"/>
<result property="sex" column="ssex"/>
<result property="age" column="sage"/>
</collection>
</resultMap>
</mapper>
associton:对象(多对一)
collection: 集合(一对多)
javaType :用来指定实体类中属性的类型
ofType :用来指定映射到List或者集合中的poji类型,
<mapper namespace="com.lmj.dao.StudentMapper">
<select id="getStudent3" resultMap="StudentTeacher2">
select s.name sname,s.age,s.sex,t.xm tname
from student s,teacher t
where s.tid=t.tid;
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="name" column="sname"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<association property="teacher" javaType="Teacher">
<result property="xm" column="tname"/>
</association>
</resultMap>
</mapper>
<select id="queryStudent" parameterType="map" resultType="Student">
select * from student
<where>
<if test="name!=null">
name like "%"#{
name}"%"
</if>
<if test="sex!=null">
and sex=#{
sex}
</if>
</where>
</select>
测试代码
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
StudentMapper mapper1 = sqlSession.getMapper(StudentMapper.class);
// int res= mapper1.addStudent(new Student(IDutils.getId(),"lmj",21,"男"));
HashMap map = new HashMap();
map.put("name","test");
map.put("sex","男");
List<Student> students = mapper1.queryStudent(map);
for (Student teacher : students) {
System.out.println(teacher);
}
sqlSession.close();
where元索只会在至少有一个子元素的条件返回SQL子句的情况下才去插入“WHERE"子句。且,若语句的开头为"AND"或“OR",where 元索也会将它们去除。
1.我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
2.将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查
询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
1.MyBatis系统中默认定义了两级缓存: 一级缓存和二级缓存
2.默认情况下,只有一 级缓存开启。 (SqlSession级别的缓存, 也称为本地缓存)
3.二级缓存需要手动开启和配置,他是基于namespace级别的缓存,通过实现Cache接口来自定义二级缓存
数据更新频繁不适合放在缓存区:设置不经过缓存useCache=“false”
缓存失效
1.查询不同的东西
2.增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
3.查询不同的Mapper .xml
4.手动清理缓存!(调用sqlsession.clearCache()方法)
●Mysq|引擎
●InnoDB底层原理
●索引
●索引优化!