今天正式开始学习SSM框架,首先学习Mybatis框架
提示:以下是本篇文章正文内容,下面案例可供参考
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
代码如下:
<dependencies>
<dependency>
<!--引入数据库依赖-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</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>
<scope>test</scope>
</dependency>
</dependencies>
<?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"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
从 XML 中构建 SqlSessionFactory,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
三句代码固定的:
String resource = “org/mybatis/example/mybatis-config.xml”;
InputStream inputStream =
Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory
= new SqlSessionFactoryBuilder().build(inputStream);
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//获取sqlSessionFactory对象
//每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的
String resource="mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
* SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
* 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
* */
//获得sqlSession对象
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
首先建出和数据库中的表对应的实体类
然后在Dao包中编写类的接口
Mapper(映射器)就是原来的Dao
在resources中编写与接口绑定的xml文件,相当于是接口的实现类
Mapper包中的实体类接口
public interface UserMapper {
//获取全部用户信息
public List<User> getUserList();
//根据用户Id获取用户信息
public User getUserById(int id);
//插入用户
public int addUser(User user);
//修改用户
public int updateUser(User user);
//删除用户
public int deleteUser(int id);
//模糊查询
public List<User> getUserLike(String value);
}
UserMapper.xml
<?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.night.Mapper.UserMapper">
<select id="getUserList" resultType="com.night.pojo.User">
select * from mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="com.night.pojo.User">
select * from mybatis.user where id=#{
id}
</select>
<insert id="addUser" parameterType="com.night.pojo.User">
insert into mybatis.user(id,name,password) values (#{
id},#{
name},#{
password})
</insert>
<update id="updateUser" parameterType="com.night.pojo.User">
update mybatis.user set name=#{
name},password=#{
password} where id=#{
id};
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{
id}
</delete>
<select id="getUserLike" resultType="com.night.pojo.User">
select *
from mybatis.user where name like #{
value};
</select>
</mapper>
并在核心配置文件config.xml中引入
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
注意:
如果是在Mapper包中写的xml文件,需要在pom.xml文件中添加以下配置。
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml
**/ *.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml
**/ *.properties</include>
</includes>
</resource>
</resources>
</build>
接口绑定xml文件,相当于是接口的实现类
在xml文件中实现 Sql语句的编写
注意:
xml文件中的namespace中的包名,要和Mapper(原来的Dao)接口的包名一样
选择,查询语句
id :就是对应的namespace中的方法名
resultType:sql语句执行的返回值
parameterType:参数类型
<select id="getUserById" parameterType="int" resultType="com.night.pojo.User">
select * from mybatis.user where id=#{
id}
</select>
在写测试代码时,这两句代码不变
SqlSession sqlSession = MybatisUtils.getSqlSession();
/*
其他代码
*/
sqlSession.close();
sqlSession用完后一定要关闭,不然会很浪费资源
//增删改需要提交事务
@Test
public void test3() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapperAdd = sqlSession.getMapper(UserMapper.class);
int i = mapperAdd.addUser(new User(4, "赵六", "000000"));
if(i>0){
System.out.println("插入成功");
}
//提交事务
sqlSession.commit();
sqlSession.close();
}
也可以在工具类返回SqlSession的方法中设置自动提交事务
//这样SqlSession会自动提交事务
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
假设实体类或者数据库中的表、字段或者参数过多,可以用map作为参数
HashMap<String, Integer> map = new HashMap<>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userByLimit = mapper.getUserByLimit(map);
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
注意:
各个属性的插入顺序必须严格按照这个顺序来,不然会报错
尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
<-- 环境的默认选择!-->
<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>
mybatis中有两种默认的事务管理器:JDBC、MANAGED
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
POOLED(默认)– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
创建db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
注意!!!:
在写db.properties时,url的路径和在核心配置文件中的写法不一样
核心配置文件中的写法为:
"jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"
有转义符&
而db.properties中的写法为:
jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
这些属性可以在外部进行配置,并可以进行动态替换。
<!--从外部引入资源,可以动态的进行替换-->
<properties resource="db.properties"></properties>
<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>
型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
例如以前:
resultType="com.company.pojo.User"
可以给实例类起别名
1.在config.xml文件中配置:
<typeAlias type="com.company.pojo.User" alias="User"></typeAlias>
2.也可以指定一个包名,Mybatis会在包名下面搜索需要的Java Bean,比如:扫描实体类的包,它的默认别名就为这个类的别名,首字母小写
<typeAliases>
<package name="com.company.pojo"/>
</typeAliases>
MapperRegistry:注册绑定我们的Mapper文件:
1.使用相对于类路径的资源引用
(推荐使用)(都在resourese目录下)目录间是斜杠不是点
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
2.使用完全限定资源定位符(URL):
(不推荐使用)
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
3.使用映射器接口实现类的完全限定类名
(推荐使用)需要将xml文件与接口放在同一个包下
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
4.将包内的映射器接口实现全部注册为映射器
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
注意点:
接口和它的Mapper配置文件必须同名
接口和它的Mapper配置文件必须在同一个包下
生命周期和作用域是至关重要的,因为错误的使用会导致很严重的并发问题
SqlSessionFactoryBuilder:
一旦创建了SqlSessionBuilderFactory,就不需要它了
作用范围最好是局部变量
SqlSessionFactory:
说白一点就可以想象为:数据库连接池
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或者重新创建另一个实例
因此SqlSessionFactory的最佳作用域就是应用作用域
最简单的就是使用单例模式或者静态单例模式
SqlSession:
连接到连接池的一个请求
SqlSession的实例不是线程安全的,因此是不能被共享的,所以他的最佳作用域是请求或者方法作用域
用完之后要赶紧关闭,否则资源要被占用
当实体类中的属性名与数据库中的字段名不一样时
在执行一些增删查改的操作时,会造成一些故障。
如查询的时候:
这是因为,在.xml文件中的查询语句
select * from mybatis.user where id=#{
id}
实际上是:
select id,name,password from mybatis.user where id=#{
id};
此时的password和pwd并不相同,以至于让返回的密码值为空
解决方法:
1.取别名:
…paaword as pwd…
2.resultMap:结果集映射
用resultMap让数据库中的字段与实体类的属性一一对应,并不需要名字一样
column:数据库中的字段 property:实体类中的属性
<resultMap id="UserMap" type="User">
<!-- column:数据库中的字段 property:实体类中的属性-->
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<result column="password" property="pwd"></result>
</resultMap>
<select id="getUserById" parameterType="_int" resultMap="UserMap">
select * from mybatis.user where id=#{
id}
</select>
其实只需要映射不一样的字段
如果一个数据库操作引起了异常,我们需要排错。日志就是最好的助手
**logImpl :**指定Mybatis创建具有延迟加载能力的对象所用到的代理工具
SLF4J
LOG4J
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING (掌握)
NO_LOGGING
STDOUT_LOGGING标准日志输出
在Mybatis核心文件中,配置我们的日志
严格遵循顺序
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!--设置日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/> <!--name和value的值不能乱加空格和乱改,很严格-->
</settings>
注意:
name和value的值不能乱加空格和乱改,很严格
1.什么是log4j:
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIXSyslog守护进程等
我们也可以控制每一条日志的输出格式
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
2.在pom.xml文件中先导入Log4j的包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
3.在resourse目录下创建log4j.properties
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/night.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
4.配置log4j的实现
<setting name="logImpl" value="LOG4J"/>
1.导入log4j.logger的包
2.获取日志对象,参数为当前类的class
3.日志级别
info\debug\error
分页是为了减少数据的处理量
关键字:(limit)
select * from user limit 0,2;
select * from user limit 起始序号,每页数量
1.接口;
//分页查询
public List<User> getUserByLimit(Map<String,Integer> map);
2.Mapper.xml
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{
startIndex},#{
pageSize}
</select>
3.测试:
HashMap<String, Integer> map = new HashMap<>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userByLimit = mapper.getUserByLimit(map);
RowBounds分页与limit分页的区别:
RowBounds是逻辑分页,limit是物理分页
在使用简单的sql语句时进行数据库操作时,可以使用注解开发,这样会减少代码量
在接口方法的前面使用注解,注解内容就是sql语句。
@Select("select * from mybatis.user")
public List<User> getUserList();
方法存在多个参数,所有参数前面必须加上@Param注解
注解的参数名需要和语句里的参数名一致,可以和参数名不一样
@Select("select * from mybatis.user where id=#{id2}")
public User getUserById(@Param("id2") int id);// 注解的参数名需要和语句里的参数名一致,可以和参数名不一样
不用再用xml文件实现sql语句
注意:使用注解实现增删改时,这里要改
//获得sqlSession对象,自动提交事务
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
基本类型的参数或者String类型,需要加上
引用类型不需要加上
如果只有一个基本类型,可以忽略,但建议加上
在SQL中引用的字段就是注解里的属性名
Lombok是一个插件,可以在实体类上用注解,自动写入其他方法
@Data 所有的getter 和setter方法
@AllArgsConstructor //有参构造
@NoArgsConstructor //无参构造
多对一查询:学生与老师
根据嵌套查询和子查询:
关联:association(多对一)
集合:collection (一对多)
javaType 和 ofType
javaType:用来指定实体类中的属性
ofType:用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
导包、编写配置文件、编写实体类、编写Mapper接口和Mapper.xml文件
可以用UUID类生成唯一的ID:
动态sql环境搭建:
导包、编写配置文件、编写实体类、编写Mapper接口和Mapper.xml文件
如果数据库字段中有下划线,可以在配置文件中配置让字段名自动转换为驼峰命名
<setting name="mapUnderscoreToCamelCase" value="true"/>
可以用UUID类生成唯一的ID:
return UUID.randomUUID().toString().replaceAll("-","");
}
可以根据传递的参数参查询不同的结果,只需要改变参数,而不需要改变Sql语句
动态SQL之if语句
可以根据传递的参数参查询不同的结果,只需要改变参数,而不需要改变Sql语句
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列.
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
Mybatis默认的是一级缓存。
只需在MyBatis的配置文件中,添加如下语句,就可以使用一级缓存。共有两个选项,SESSION或者STATEMENT,默认是SESSION级别,即在一个MyBatis会话中执行的所有语句,都会共享这一个缓存。一种是STATEMENT级别,可以理解为缓存只对当前执行的这一个Statement有效。
<setting name="localCacheScope" value="SESSION"/>
缓存失效的情况:
1.查询不同的东西
2.增删查改操作,可能会改变原来的数据,所以必定会刷新缓存
3.查询不同的Mapper.xml
4.手动清理缓存 clearCache()方法
小结:一级缓存是默认开启的,只在一次SQLSession中有效,也就是拿到连接到关闭这个区间。
MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。
如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。
二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。
二级缓存的工具示意图:
当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。
二级缓存配置:
1.在config.xml文件中设置二级缓存开启
<setting name="cacheEnabled" value="true"/>
在实体类接口的xml文件中配置:
cache这个标签可以设置其他参数:
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
(1) LRU,最近最少使用的,一处最长时间不用的对象
(2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
(3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
(4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,
移除最长时间不用的对形象
flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当
SQL被执行的时候才会去刷新缓存。
size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象
readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,他的默认值是false,不允许我们修改