MyBatis
参考:https://www.w3cschool.cn/mybatis3/mybatis3-rhna3ndr.html
环境:
-
JDK
-
MySQL
-
Maven
-
IDEA
1、简介
1.1 什么是MyBatis
-
MyBatis 是一款优秀的持久层框架
-
它支持自定义 SQL、存储过程以及高级映射。
-
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
-
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和POJO(Plain Ordinary Java Object,简单的Java对象)为数据库中的记录。
1.2 持久划
数据持久化
-
持久化就是将程序的数据在持久状态和瞬时状态转化的过程
-
内存:断电即失
-
数据库(JDBC),IO文件持久化。
1.3 持久层
-
完成持久化工作的代码
-
层界限十分明显
2、第一个MyBatis程序
思路:搭建环境-->导入MyBatis-->编写代码-->测试
新建项目:
-
新建一个普通maven项目
-
删除src目录
-
导入maven依赖
创建一个module
-
编写MyBatis的核心配置文件
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/><!--${driver}-->
<property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=true&useUnicode=true&characterEncoding=utf-8"/><!--${url}-->
<property name="username" value="root"/><!--${username}-->
<property name="password" value="stujc0828"/><!--${password}-->
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/stujc/dao/UserMapper.xml"/>
</mappers>
</configuration>
-
编写MyBatis工具类
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
} -
编写代码
-
实体类
-
Dao接口
-
public interface UserDao {
List<User> getUserList();
}
-
-
接口实现类
-
<?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.stujc.dao.UserDao">
<select id="getUserList" resultType="com.stujc.pojo.User">
select * from student s
/*select * from Blog where id = #{id}*/
</select>
</mapper>
-
-
测试
public class UserDaoTest {
@Test
public void test(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
3、CRUD
选择,查询语句
-
id:就是对应的namespace中的方法
-
resultType:SQL语句执行的返回值
-
parameterType:参数类型
-
编写接口
public interface UserMapper {
//查询全部用户
List<User> getUserList();
//根据id查询用户
User getUserById(int id);
//插入一个用户
int insertUser(User user);
//修改用户
int updateUser(User user);
//删除用户
int deleteUser(int id);
}
-
编写mapper中对应的sql语句
<mapper namespace="com.stujc.dao.UserMapper">
<select id="getUserList" resultType="com.stujc.pojo.User">
select * from users;
</select>
<select id="getUserById" resultType="com.stujc.pojo.User" parameterType="int">
select * from users where id = #{id};
</select>
<insert id="insertUser" parameterType="com.stujc.pojo.User">
insert into users (id,name,password,email,birthday) values (#{id},#{name},#{password},#{email},#{birthday});
</insert>
<update id="updateUser" parameterType="com.stujc.pojo.User">
update users set name = #{name} where id = #{id};
</update>
<delete id="deleteUser" parameterType="int">
delete from users where id = #{id};
</delete>
</mapper>
-
测试
-
注意点:增删改需要提交事务
public class UserDaoTest {
@Test
public void test(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
@Test
public void test1(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println("test1:"+user);
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
@Test
public void test2(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
User user = new User(4,"zhaoliu","123456","[email protected]",java.sql.Date.valueOf("1980-12-04"));
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.insertUser(user);
System.out.println("test2:"+i);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
@Test
public void test3(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
User user = new User(4,"田七","123456","[email protected]",java.sql.Date.valueOf("1980-12-04"));
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.updateUser(user);
System.out.println("test3:"+i);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
@Test
public void test4(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.deleteUser(4);
System.out.println("test3:"+i);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
4、配置解析
4.1 核心配置文件
-
mybatis-config.xml
4.2 环境配置
MyBatis 可以配置成适应多种环境
尽管可以配置多个环境,但每个SqlSessionFactory
实例只能选择一种环境
MyBatis默认的事务管理器就是JDBC,连接池:POOLED
4.3 属性(Properties)
既可以在典型的 Java 属性文件中配置这些属性,也可以在properties
元素的子元素中设置。【db.properties】
-
编写配置文件
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/school?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=stujc0828
-
在核心配置文件中引入
<!--引入外部配置文件-->
<properties resource="db.properties"/>
4.4 类型别名(typeAliases)
-
类型别名可为 Java 类型设置一个缩写名字
-
意在降低冗余的全限定类名书写
<typeAliases> <typeAlias type="com.stujc.pojo.User" alias="User"/> </typeAliases>
-
也可以指定一个包名,MyBatis 会在包名下面搜索需要的
Java Bean
<!--默认别名class首字母小写--> <package name="com.stujc.pojo"/>
-
若有注解,则别名为其注解值
@Alias("author")
4.5 设置
MyBatis 中极为重要的调整设置,会改变 MyBatis 的运行时行为
4.6 映射器(mappers)
方式一
<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> </mappers>
4.7 生命周期和作用域
生命周期和作用域,是至关重要的,因为错误的使用会导致非常严重的并放问题
5、ResultMap
解决属性名和字段名不一致的问题
<resultMap id="UserMap" type="User"> <!--column数据库中的字段,property实体类中的属性--> <result column="" property=""/> </resultMap> <select id="getUserById" resultMap="UserMap" parameterType="int"> select * from users where id = #{id}; </select>
6、日志
6.1 日志工厂
如果一个数据库操作出现了一场,需要排错,日志就是最好的助手
logImpl:
-
SLF4J
-
LOG4J(deprecated since 3.5.9)
-
LOG4J2
-
JDK_LOGGING
-
COMMONS_LOGGING
-
STDOUT_LOGGING (标准日志输出)
-
NO_LOGGING
在MyBatis中,具体使用哪一个,在设置中设定
<!--设置-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Opening JDBC Connection
Created connection 365590665.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@15ca7889]
==> Preparing: select * from users;
==> Parameters:
<== Columns: id, name, password, email, birthday
<== Row: 1, zhangsan, 123456, [email protected], 1980-12-04
<== Row: 2, lisi, 123456, [email protected], 1981-12-04
<== Row: 3, wangwu, 123456, [email protected], 1979-12-04
<== Row: 4, 田七, 123456, [email protected], 1980-12-04
<== Total: 4
User{id=1, name='zhangsan', password='123456', email='[email protected]', birthday=Thu Dec 04 00:00:00 CST 1980}
User{id=2, name='lisi', password='123456', email='[email protected]', birthday=Fri Dec 04 00:00:00 CST 1981}
User{id=3, name='wangwu', password='123456', email='[email protected]', birthday=Tue Dec 04 00:00:00 CST 1979}
User{id=4, name='田七', password='123456', email='[email protected]', birthday=Thu Dec 04 00:00:00 CST 1980}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@15ca7889]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@15ca7889]
Returned connection 365590665 to pool.
6.2 Log4J
-
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
-
可以控制每一条日志的输出格式
-
通过定义每一条日志的级别,能够更加细致的控制日志的生成过程
-
通过一个配置文件灵活的进行配置,不需要修改应用的代码
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.20.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.20.0</version> </dependency>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- log4j2 配置文件 --> <!-- 日志级别 trace<debug<info<warn<error<fatal --> <configuration status="debug"> <!-- 自定义属性 --> <Properties> <!-- 日志格式(控制台) --> <Property name="pattern1">[%-5p] %d %c - %m%n</Property> <!-- 日志格式(文件) --> <Property name="pattern2"> =========================================%n 日志级别:%p%n 日志时间:%d%n 所属类名:%c%n 所属线程:%t%n 日志信息:%m%n </Property> <!-- 日志文件路径 --> <Property name="filePath">F:\logs/myLog.log</Property> </Properties> <appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="${pattern1}"/> </Console> <RollingFile name="RollingFile" fileName="${filePath}" filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout pattern="${pattern2}"/> <SizeBasedTriggeringPolicy size="5 MB"/> </RollingFile> </appenders> <loggers> <root level="debug"> <appender-ref ref="Console"/> <appender-ref ref="RollingFile"/> </root> </loggers> </configuration>
7、分页
<!--分页查询用户-->
<select id="getUserByLimit" resultType="User" parameterType="map">
select * from users limit #{startIndex},#{pageSize}
</select>
<!--分页查询用户2-->
<select id="getUserByRowBounds" resultType="User">
select * from users;
</select>
@Test
public void getUserByLimit(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
RowBounds rowBounds = new RowBounds(1,2);
List<User> userList = sqlSession.selectList("com.stujc.dao.UserMapper.getUserByRowBounds",null, rowBounds);
for (User user : userList) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
8、注解开发
8.1 面向接口编程
关于接口的理解
-
接口从更深层次的理解,应是定义(规范、约束)与实现(名实分离的原则)的分离
-
接口的本身反映了系统设计人员对系统的抽象理解
-
接口应有两类:
-
第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class)
-
第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface)
一个个体有可能有多个抽象面。抽象体与抽象面是有区别的
-
8.2 使用注解开发
-
注解在接口上实现
public interface UserMapper { @Select("select * from users") List<User> getUserList(); //方法存在多个参数,所有的参数前面必须加上@Param()注解 @Select("select * from users where id = #{id} and name = #{name}") User getUserByID(@Param("id") int id,@Param("name") String name); }
-
需要再核心文件中绑定接口
<mappers>
<mapper class="com.stujc.dao.UserMapper"/>
</mappers> -
测试
@Test
public void getUserList(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
//底层主要应用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for(User user : userList){
System.out.println(user);
}
sqlSession.close();
}
本质:反射机制实现
底层:动态代理
9、LomBox
@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder @SuperBuilder @Singular @Jacksonized @Delegate @Value @Accessors @Tolerate @Wither @With @SneakyThrows
10、缓存
什么是缓存
-
存在内存中的临时数据
-
将用户经常查询的数据放在缓存(内存)中,用户去查数据就不用从磁盘上查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
为什么使用缓存
-
减少和数据库的交互次数,减小系统开销,提高系统效率
什么样的数据能使用缓存
-
经常查询且不经常改变的数据
10.1 mybatis缓存
Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
-
默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
-
二级缓存需要手动开启和配置,它是基于namespace级别的缓存
-
为了提高扩展性,Mybatis定义了缓存接口Cache,可以通过实现Cache接口来自定义二级缓存
10.2 一级缓存
测试步骤
-
开启日志
-
测试在一个SqlSession中查询两次相同的记录
-
查看日志输出
一级缓存是默认开启的,只在一次SqlSession中有效。
一级缓存就是一个map
10.3 二级缓存
-
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间,对应一个二级缓存
-
工作机制
-
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
-
如果会话关闭了,这个会话对应的一级缓存就没了;想要实现的效果是,会话关闭了,一级缓存中的数据被保存到二级缓存中
-
新的会话查询信息,就可以从二级缓存中获取内容
-
不同的mapper查出的数据会放在自己对应的缓存(map)中
-
步骤:
-
开启全局缓存
<!--显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
-
在要使用二级缓存的Mapper中开启
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
-
测试
-
问题:需要将实体类序列化,否则会报错
-
10.4 自定义缓存
Ehcache是一种广泛使用的开源Java分布式缓存,主要面向通用缓存
<?xml version="1.0" encoding="UTF-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:缓存路径,ehcache分为内存和磁盘两极,此属性定义磁盘的缓存位置
user.home - 用户主目录
user.dir - 用户当前工作目录
java.io.tmdir - 默认临时文件路径
-->
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
<!--
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略,只能定义一个
-->
<!--
name:缓存名称
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数
external:对象是否永久有效,一旦设置了,timeout将不起作用
overflowToDisk:是否保存到磁盘,当系统宕机时
-->
</ehcache>
<!--mapper-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
MyBatis加载机制
-
Resources获取全局配置文件
-
实例化SqlSessionFactoryBuilder构造器
-
解析配置文件流XmlConfigBuilder
-
Configuration所有的配置信息
-
SqlSessionFactory实例化
-
transaction事务管理
-
创建executor执行器
-
创建sqlsession
-
实现CRUD,查看执行结果
-
提交事务,关闭
标签:mapper,缓存,UserMapper,持续性,id,sqlSession,user,MyBatis,Day19 From: https://www.cnblogs.com/-Gin/p/18259196