【Mybatis】(一)
Mybatis简介
提供持久层框架包括SQL Maps和Data Access Objects(DAO).
SQL Maps提供数据库数据和java数据的映射关系,换句话说即是封装JDBC的过程。
Data Access Objects数据访问对象,通过JDBC访问数据库然后操作数据库中的数据
Mybatis特性
1) MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
高级映射指的是数据库字段名和java属性名不一致时的映射,相反只能处理相同情况的映射称为普通映射
2) MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
3) MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java
Objects,普通的Java对象)映射成数据库中的记录
4) MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架
和其他持久层技术的对比
①JDBC
SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
代码冗长,开发效率低
②Hibernate 和 JPA
操作简便,开发效率高
程序中的长难复杂 SQL 需要绕过框架
内部自动生产的 SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
反射操作太多,导致数据库性能下降
③MyBatis
轻量级,性能出色
SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
开发效率稍逊于HIbernate,但是完全能够接受
搭建Mybatis
开发环境
IDE:idea 2020.2
构建工具:maven 3.6.3
MySQL版本:MySQL 5.0.12
MyBatis版本:MyBatis 3.5.7
创建Maven工程
a>打包方式:jar
b>引入依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-mxj-db-files</artifactId>
<version>5.0.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>
配置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.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="abc123"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件 -->
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
创建mapper接口 UserMapper
mapper接口相当于以前的DAO,但是区别在于mapper仅仅是接口,不需要提供额外的实现类,因为mybatis里有面向接口编程,每当调用接口中的方法,mybatis就会自动执行相应的sql语句
public interface UserMapper {
int insertUser();
}
创建同名映射文件 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.hikaru.mybatis.mapper.UserMapper">
<insert id="insertUser">
insert into user(user, balance) values("2b", 1000)
</insert>
</mapper>
1、映射文件的命名规则:
表所对应的实体类的类名+Mapper.xml
例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
因此一个映射文件对应一个实体类,对应一张表的操作
MyBatis映射文件用于编写SQL,访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
2、MyBatis中可以面向接口操作数据,要保证两个一致:
a>mapper接口的全类名和映射文件的命名空间(namespace)保持一致
b>mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
在mybatis核心配置文件中引入映射文件
<?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/mydb"/>
<property name="username" value="root"/>
<property name="password" value="abc123"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件 -->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
测试:调用mapper接口方法
@Test
public void testMybatis() throws IOException {
//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//构建SqlSessionFactory
SqlSessionFactory sessionFactory = builder.build(resourceAsStream);
//获取SqlSession
SqlSession sqlSession = sessionFactory.openSession();
//返回一个接口对应的实现类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.insertUser();
sqlSession.commit();
System.out.println("result = " + result);
}
SqlSession是一个数据库与java程序的通话,底层实现了mapper的接口实现类
当mapper接口调用方法时,mybatis会到同名配置文件执行被调用方法的相应sql语句
由于mybatis核心配置文件中的<transactionManager type="JDBC"/> sql的执行本质是事务,因此需要进行事务的提交
设置事务自动提交
SqlSession sqlSession = sessionFactory.openSession(true);
对Mybatis功能的优化 添加log4j日志
添加依赖
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
加入log4j的配置文件
log4j的配置文件为log4j.xml,文件目录为src/main/resource下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS}
%m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
日志的级别
FATAL>ERROR>WARN>INFO>DEBUG
从左往右打印的内容会越来越详细
Mybatis实现增删改查
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.hikaru.mybatis.mapper.UserMapper">
<insert id="insertUser">
insert into user(user, balance) values("2b", 1000)
</insert>
<select id="getUserById" resultType="com.hikaru.mybatis.pojo.User">
select * from user where id = 3
</select>
<select id="getAllUser" resultType="com.hikaru.mybatis.pojo.User">
select * from user;
</select>
</mapper>
查询功能必须设置resultType或resultMap
resultType:设置默认的映射关系,要求数据库字段名与实体属性名一致
resultMap:设置自定义的映射关系
Mapper接口
public interface UserMapper {
int insertUser();
User getUserById();
List<User> getAllUser();
}
测试
@Test
public void testMybatis() throws IOException {
//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//构建SqlSessionFactory
SqlSessionFactory sessionFactory = builder.build(resourceAsStream);
//获取SqlSession
SqlSession sqlSession = sessionFactory.openSession(true);
//返回一个接口对应的实现类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById();
System.out.println(user);
List<User> users =mapper.getAllUser();
users.forEach(user1 -> System.out.println(user1));
}
核心配置文件详解
核心配置文件中的标签必须按照固定的顺序:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
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"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="abc123"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件 -->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
enviroments
default:表示存在多个数据库环境时,默认使用的数据库环境id
enviroment
①id:数据库环境的唯一表示
②transactionManager:设置事务管理方式。
属性:type=“JDBC”或者“MANAGED”。JDBC表示是原生的事务管理方式,事物的提交和回滚都需要手动进行。MANAGED:被管理,例如Spring
③dataSource:配置数据源
属性type=“POOLED”或者“UNPOOLED”或者“JNDI”。POOLED表示数据库连接池,UNPOOLED表示不使用数据库连接池,JNDI表示使用上下文的数据源
在Spring整合后,可以直接使用Spring的数据源
使用properties存储JDBC数据库连接信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.username=root
jdbc.password=abc123
在配置文件中使用<properities resource="">标签的形式以及${}获取properties文件的键值对
<properties resource="jdbc.properties"/>
<!--配置数据库的环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
typeAliases package添加类型别名
<typeAliases >
<typeAlias type="com.hikaru.mybatis.pojo.User" alias="user"></typeAlias>
</typeAliases>
属性type为被添加别名的类型,alias为添加的别名(不区分大小写)
如没有声明alias属性,则默认别名为类名(仍不区分大小写)
则在mybatis的范围内使用user及其大小写可以替换完整类名
<?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.hikaru.mybatis.mapper.UserMapper">
<insert id="insertUser">
insert into user(user, balance) values("2b", 1000)
</insert>
<select id="getUserById" resultType="user">
select * from user where id = 3
</select>
<select id="getAllUser" resultType="User">
select * from user;
</select>
</mapper>
也可以使用标签aliases下的标签package,替换一个包内的所有类型名为默认别名
<typeAliases >
<package name="com.hikaru.mybatis.pojo"/>
</typeAliases>
以包的形式添加mapper mappers>package
<!--引入映射文件 -->
<mappers>
<!-- <mapper resource="mappers/UserMapper.xml"/>-->
<package name="com.hikaru.mybatis.mapper"/>
</mappers>
要求映射文件的包名要和mapper接口所在包名一致
映射文件名和mapper接口名一致
资源下的包名不能用.要用/
Mybatis获取参数值的两种方式
①单个字面量的参数
若mapper接口中的方法参数为单个的字面量类型
此时可以使用${}和#{}以任意的名称获取参数的值,注意${}需要手动加单引号
<!--select * from user where user = #{aaa}-->
select * from user where user = '${aaa}'
②多个字面量类型的参数
Mapper接口:
int insertUserByNameAndBalance(String user, double balance);
Mapper映射文件:
<insert id="insertUserByNameAndBalance">
insert into user(user, balance) values (#{param1}, #{arg1})
</insert>
若mapper接口中的方法参数为多个时
此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1...为键,以参数为值;以param1,param2...为键,以参数为值; 因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
③参数为Map集合类型的数据
若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
mapper接口:
int insertUserByNameAndBalance(Map<String, Object> map);
mapper映射文件:
<insert id="insertUserByNameAndBalance">
insert into user(user, balance) values (#{user}, #{balance})
</insert>
这时原来自定义的键值param和arg都不能再用了
④实体类型的参数
若mapper接口中的方法参数为实体类对象时
此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号
mapper接口
int insertUser(User user);
mapper映射文件
<insert id="insertUser">
insert into user(user, balance) values(#{user}, #{balance})
</insert>
这里的属性名狭义上讲是实体类get、set方法的名称得到小写,而不是成员变量,要严格对应get、set方法的名称
⑤使用@Param标识参数
以通过@Param注解标识mapper接口中的方法参数
此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以
param1,param2...为键,以参数为值;只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
mapper接口
int insertUser(@Param("username") String username, @Param("balance") double balance);
mapper映射文件
<insert id="insertUser">
insert into user(user, balance) values(#{username}, #{balance})
</insert>
<insert id="insertUser">
insert into user(user, balance) values(#{param1}, #{param2})
</insert>
这里相较于③map集合的方式,保留了param的键值对
Mybatis的各种查询功能
查询出的数据只有一条时,可以通过 ①实体类②list集合③map集合 接收
map集合形式为:{balance=1000, id=1, user=2b},这种方法方便将数据转换为json的形式
查询的数据有多条时,一定不能通过实体类对象接收,否则会报TooManyResultException错误
查询单个数据
mapper接口
int getCount();
mapper映射文件
<select id="getCount" resultType="int">
select count(*) from user;
</select>
Alias Mapped Type
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iteratot
collection
iterator
Mybatis内置的别名
Map类型查询结果
单个数据结果转换为Map
map接口:
Map<String, Object> getUserIdToMap(@Param("id") int id);
map映射文件:
<select id="getUserIdToMap" resultType="map">
select *
from user
where id = #{id};
</select>
多个数据结果转换为Map
①通过List<Map>的形式
mapper接口
List<Map<String, Object>> list = mapper.getUserIdToMap();
mapper配置文件
<select id="getUserIdToMap" resultType="map">
select *
from user;
</select>
结果为map类型的list集合:
[{balance=1000, id=1, user=2b}, {balance=1000, id=2, user=9s}, {balance=1000, id=3, user=9s},
②通过@MapKey()的形式
mapper接口
@MapKey("id")
Map<String, Object> getUserIdToMap();
mapper映射文件
<select id="getUserIdToMap" resultType="map">
select *
from user;
</select>
结果为map类型的map,键为多个map获取的某一属性值,值为获取的多个map集合:
{1={balance=1000, id=1, user=2b}, 2={balance=1000, id=2, user=9s}, 3={balance=1000, id=3, user=9s},
Mybatis特殊查询
模糊查询
批量删除 in
mapper接口
int deleteMore(@Param("ids") String ids);
mapper映射文件
<delete id="deleteMore">
delete
from user
where id in (${ids})
</delete>
in批量删除只能使用$,#的话会自动添加双引号导致sql语句不正确
动态获取表名
mapper接口
List<User> seleceByTableName(@Param("tableName") String tableName);
mapper配置文件
<select id="seleceByTableName" resultType="user">
select *
from ${tableName}
只能使用${}
添加功能获取自增的主键
mapper接口
int deleteMore(@Param("ids") String ids);
mapper配置文件
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into user(user, balance) values(#{user}, #{balance})
</insert>
userGeneratedKeys设置当前标签中的sql是否使用了主键自增
keyProperty将自增的主键赋值给传输到配置文件的参数的某个属性
test
@Test
public void testMybatis() throws IOException {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User("ryuu", 10000, null);
mapper.insertUser(user);
System.out.println(user);
}
输出
User{user='ryuu', balance=10000.0, id=14}
解决多对一和一对多映射
”对一对应对象,对多对应集合“
自定义映射resultMap
字段名和属性名不一致的输出
Emp{eid=1, empName='null', sex='男', email='123@qq.com', did=1}
Emp{eid=2, empName='null', sex='女', email='123@qq.com', did=2}
Emp{eid=3, empName='null', sex='男', email='123@qq.com', did=3}
Emp{eid=4, empName='null', sex='女', email='123@qq.com', did=1}
Emp{eid=5, empName='null', sex='男', email='123@qq.com', did=2}
mapUnderscoreToCamelCase表示将数据库字段名_映射为java驼峰式属性名,解决字段名和属性名的映射关系
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
通过级联复制解决多对一的映射关系
mapper接口
List<Emp> getEmpAndDept(String eid);
mapper映射文件
<resultMap id="empAndDeptResultMap1" type="emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<result property="dept.did" column="did"></result>
<result property="dept.deptName" column="dept_name"></result>
</resultMap>
<select id="getEmpAndDept" resultMap="empAndDeptResultMap1">
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid};
</select>
测试结果
Emp{eid=2, empName='2B', sex='女', email='123@qq.com', age='22', dept=Dept{did=2, deptName='B'}}
通过association解决多对一映射
mapper接口
List<Emp> getEmpAndDept2(String eid);
mapper配置文件
<resultMap id="empAndDeptResultMap2" type="emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<association property="dept" javaType="dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
<select id="getEmpAndDept2" resultMap="empAndDeptResultMap2">
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid};
</select>
测试结果
Emp{eid=2, empName='2B', sex='女', email='123@qq.com', age='22', dept=Dept{did=2, deptName='B'}}
分布查询解决多对一的映射关系
mapper接口
List<Emp> getEmpAndDeptStep1(@Param("eid") String eid);
Dept getEmpAndDeptStep2(String did);
mapper映射文件
<resultMap id="empAndDeptResultMap" type="emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<association property="dept"
select="com.hikaru.mybatis.mapper.DeptMapper.getEmpAndDeptStep2"
column="did">
</association>
</resultMap>
<select id="getEmpAndDeptStep1" resultMap="empAndDeptResultMap">
select * from t_emp where eid = #{eid};
</select>
select表示调用其他表对应的映射接口的方法
<select id="getEmpAndDeptStep2" resultType="dept">
select * from t_dept where did = #{did};
</select>
这里没有对dept映射做ResultMap处理,是因为mybatis核心配置中开启了下划线到驼峰的自动映射
结果
Emp{eid=2, empName='2B', sex='女', email='123@qq.com', age='22', dept=Dept{did=2, deptName='B'}}
分布查询的延迟加载 lazyLoadingEnabled
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
理解延迟加载:并不是开启后只会执行第一步的查询而不执行第二步,而是需要执行第二步的时候才去执行第二步
测试①:
Emp emp = empMapper.getEmpAndDeptStep1("2");
System.out.println(emp.getEmpName());
输出:
DEBUG 05-04 13:04:48,410 ==> Preparing: select * from t_emp where eid = ?; (BaseJdbcLogger.java:137)
DEBUG 05-04 13:04:48,437 ==> Parameters: 2(String) (BaseJdbcLogger.java:137)
DEBUG 05-04 13:04:48,495 <== Total: 1 (BaseJdbcLogger.java:137)
2B
测试②:
Emp emp = empMapper.getEmpAndDeptStep1("2");
System.out.println(emp.getDept());
输出:
DEBUG 05-04 13:06:45,414 ==> Preparing: select * from t_emp where eid = ?; (BaseJdbcLogger.java:137)
DEBUG 05-04 13:06:45,446 ==> Parameters: 2(String) (BaseJdbcLogger.java:137)
DEBUG 05-04 13:06:45,567 <== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 05-04 13:06:45,569 ==> Preparing: select * from t_dept where did = ?; (BaseJdbcLogger.java:137)
DEBUG 05-04 13:06:45,569 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:137)
DEBUG 05-04 13:06:45,571 <== Total: 1 (BaseJdbcLogger.java:137)
Dept{did=2, deptName='B'}
如上第二组测试中需要查询第二步SQL的时候,延迟加载才会去执行第二步
fetchType标签单独设置延迟加载
fetchType值为eager或者lazy
<association property="dept"
select="com.hikaru.mybatis.mapper.DeptMapper.getEmpAndDeptStep2"
column="did"
fetchType="eager">
</association>
测试输出:
DEBUG 05-04 13:11:48,340 ==> Preparing: select * from t_emp where eid = ?; (BaseJdbcLogger.java:137)
DEBUG 05-04 13:11:48,365 ==> Parameters: 2(String) (BaseJdbcLogger.java:137)
DEBUG 05-04 13:11:48,383 ====> Preparing: select * from t_dept where did = ?; (BaseJdbcLogger.java:137)
DEBUG 05-04 13:11:48,384 ====> Parameters: 2(Integer) (BaseJdbcLogger.java:137)
DEBUG 05-04 13:11:48,385 <==== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 05-04 13:11:48,387 <== Total: 1 (BaseJdbcLogger.java:137)
2B
解决多对一的映射关系
”对一对应对象,对多对应集合“
collection解决多对一的映射关系
mapper接口
Dept getDeptAndEmp(@Param("did") String did);
mapper映射文件
<resultMap id="deptAndEmpResultMap" type="dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps" ofType="emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</collection>
</resultMap>
<select id="getDeptAndEmp" resultMap="deptAndEmpResultMap">
select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did};
</select>
collection标签用于解决多对一的映射关系,与association需要javatype获取对象类型不同,collection需要ofType获取集合内的对象类型
集合内对象不能再声明Dept类,防止套娃
通过分布查询解决多对一映射
mapper接口
Dept getDeptAndEMmpStep1(@Param("did") String did);
List<Emp> getDeptAndEmpStep2(@Param("did") String did);
mapper映射文件
<resultMap id="deptAndEMmpStep1RS" type="dept">
<id property="did" column="did"></id>
<id property="deptName" column="dept_name"></id>
<collection property="emps"
select="com.hikaru.mybatis.mapper.EmpMapper.getDeptAndEmpStep2"
column="did"></collection>
</resultMap>
<select id="getDeptAndEMmpStep1" resultMap="deptAndEMmpStep1RS">
select * from t_dept where did = #{did}
</select>
<select id="getDeptAndEmpStep2" resultType="emp">
select *
from t_emp where did = #{did};
</select>
动态SQL
Mybatis框架的动态SQL技术是一种根据条件动态拼装SQL语句的功能,它存在的意义是为了解决动态拼接SQL语句字符串的痛点问题。
if标签
mapper接口
Emp getEmpByCondition(Emp emp);
mapper映射文件
<select id="getEmpByCondition" resultType="emp">
select * from t_emp where 1 = 1
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="sex != null and sex != ''">
and sex = #{sex}
</if>
<if test="email != null and email != ''">
and email = #{email}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
</select>
使用恒成立条件1=1防止sql语句and前缀错误
测试输出
DEBUG 05-05 09:11:48,799 ==> Preparing: select * from t_emp where 1 = 1 and emp_name = ? and sex = ? and email = ? (BaseJdbcLogger.java:137)
DEBUG 05-05 09:11:48,824 ==> Parameters: 1A(String), 男(String), 123@qq.com(String) (BaseJdbcLogger.java:137)
DEBUG 05-05 09:11:48,847 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='1A', sex='男', email='123@qq.com', age='23', dept=null}
Process finished with exit code 0
where标签
mapper映射文件
<select id="getEmpByCondition" resultType="emp">
select * from t_emp
<where>
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="sex != null and sex != ''">
and sex = #{sex}
</if>
<if test="email != null and email != ''">
and email = #{email}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
</where>
</select>
当where标签中有内容时,会自动生成where关键字,并将内容前多余的and、or去掉
当where标签中没有内容时,where标签不会生成where关键字
where标签不能将内容后面的and、or去掉
trim标签
trim若标签中有内容时:
prefix|suffix:将trim标签中内容前面或者后面添加指定的内容
prefixOverrides|suffixOverrides:将trim标签中内容前面或者后面去掉指定的内容
若标签中没有内容:trim标签也没有任何的效果
mapper映射文件
<select id="getEmpByCondition" resultType="emp">
select * from t_emp
<trim prefix="where" suffixOverrides="and">
<if test="empName != null and empName != ''">
emp_name = #{empName} and
</if>
<if test="sex != null and sex != ''">
sex = #{sex} and
</if>
<if test="email != null and email != ''">
email = #{email} and
</if>
<if test="age != null and age != ''">
age = #{age} and
</if>
</trim>
</select>
输出
DEBUG 05-05 09:36:20,141 ==> Preparing: select * from t_emp (BaseJdbcLogger.java:137)
DEBUG 05-05 09:36:20,172 ==> Parameters: (BaseJdbcLogger.java:137)
DEBUG 05-05 09:36:20,196 <== Total: 5 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='1A', sex='男', email='123@qq.com', age='23', dept=null}
Emp{eid=2, empName='2B', sex='女', email='123@qq.com', age='22', dept=null}
Emp{eid=3, empName='3S', sex='男', email='123@qq.com', age='23', dept=null}
Emp{eid=4, empName='9S', sex='女', email='123@qq.com', age='24', dept=null}
Emp{eid=5, empName='1T', sex='男', email='123@qq.com', age='22', dept=null}
choose:when、otherwise
when、otherwise相当于if if if ... else
foreach
属性collection表示需要循环的数组或集合,item为数组或集合中的每一个数据
separator表示循环体之间的分隔符
open|close可以在标签中的内容的开始和结束添加指定内容
foreach实现批量删除
mapper接口
int deleteMore(@Param("eids") Integer[] ids);
mapper映射文件
<delete id="deleteMore">
delete from t_emp where eid in
<foreach collection="eids" separator="," item="eid" open="(" close=")">
#{eid}
</foreach>
</delete>
测试输出
DEBUG 05-05 10:11:59,303 ==> Preparing: delete from t_emp where eid in ( ? , ? ) (BaseJdbcLogger.java:137)
DEBUG 05-05 10:11:59,329 ==> Parameters: 6(Integer), 7(Integer) (BaseJdbcLogger.java:137)
DEBUG 05-05 10:11:59,331 <== Updates: 0 (BaseJdbcLogger.java:137)
foreach实现批量添加
mapper接口
int insertMore(@Param("emps") List<Emp> emps);
mapper配置文件
<insert id="insertMore">
insert into t_emp (<include refid="empColumns"/>) values
<foreach collection="emps" item="emp" separator=",">
(null, #{emp.empName}, #{emp.sex}, #{emp.email}, 1)
</foreach>
</insert>
这里的include是引用sql片段
设置sql片段
<sql id="empColumns">eid, emp_name, sex, email, did</sql>
测试
@Test
public void testDynamicSQLMapper() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class)
//Integer eid, String empName, String sex, String email, Integer did
Emp emp1 = new Emp(null, "a1", "男", "123@qq.com");
Emp emp2 = new Emp(null, "a2", "男", "123@qq.com");
Emp emp3 = new Emp(null, "a3", "男", "123@qq.com");
List<Emp> emps = Arrays.asList(emp1, emp2, emp3);
int result = mapper.insertMore(emps);
System.out.println(result);
}
Mybatis缓存
缓存只适用于查询
Mybatis一级缓存
一级缓存是sqlSession级别,自动开启
令一级缓存失效的情况:
①不同的SqlSession对应不同的一级缓存
②同一个SqlSession但是查询条件不同
③同一个SqlSession两次查询期间执行了任何一次增删改操作
④同一个SqlSession两次查询期间手动清空了缓存,sqlsession.clearCache()
测试
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
List<Emp> emps = mapper.getAll();
emps.forEach(emp -> System.out.println(emp));
SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
List<Emp> emps1 = mapper1.getAll();
emps1.forEach(emp -> System.out.println(emp));
结果为两次sql查询
DEBUG 05-05 11:20:12,191 ==> Preparing: select * from t_emp; (BaseJdbcLogger.java:137)
DEBUG 05-05 11:20:12,213 ==> Parameters: (BaseJdbcLogger.java:137)
DEBUG 05-05 11:20:12,231 <== Total: 11 (BaseJdbcLogger.java:137
DEBUG 05-05 11:20:12,297 ==> Preparing: select * from t_emp; (BaseJdbcLogger.java:137)
DEBUG 05-05 11:20:12,297 ==> Parameters: (BaseJdbcLogger.java:137)
DEBUG 05-05 11:20:12,300 <== Total: 11 (BaseJdbcLogger.java:137) )
Mybatis二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询(即使SqlSession不同)的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件:
a> 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="cacheEnabled" value="true"/>
</settings>
b> 在mapper映射文件中设置标签cache
<cache />
c> 二级缓存必须在SqlSession关闭或提交之后有效
d> 查询的数据所转换的实体类类型必须实现序列化的接口
即是使实体类继承Serializable接口
使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
测试
@Test
public void testCacheTwo() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
List<Emp> emps1 = mapper1.getAll();
emps1.forEach(emp -> System.out.println(emp));
sqlSession1.close();
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
List<Emp> emps2 = mapper2.getAll();
emps2.forEach(emp -> System.out.println(emp));
sqlSession2.close();
}
测试结果:
DEBUG 05-06 10:30:39,916 Cache Hit Ratio [com.hikaru.mybatis.mapper.CacheMapper]: 0.0 (LoggingCache.java:60)
DEBUG 05-06 10:30:40,800 ==> Preparing: select * from t_emp; (BaseJdbcLogger.java:137)
DEBUG 05-06 10:30:40,822 ==> Parameters: (BaseJdbcLogger.java:137)
DEBUG 05-06 10:30:40,846 <== Total: 11 (BaseJdbcLogger.java:13
7)
DEBUG 05-06 10:34:31,559 Cache Hit Ratio [com.hikaru.mybatis.mapper.CacheMapper]: 0.5 (LoggingCache.java:60)
可以看到只查询了一次SQL,第二次缓存利用率为0.5
Mybatis二级缓存的相关配置
eviction属性:缓存回收策略
缓存是缓存到内存中的,故不能无限制地缓存,因此需要设定策略定期清除
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
flushInterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用增删改语句时刷新
size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
MyBatis缓存查询的顺序
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据(因为二级缓存对应多个SQLSession,范围更大),可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存
第三方缓存EHCache
a>添加依赖
<!-- Mybatis EHCache整合包 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
b>各jar包功能
jar包名称 作用
mybatis-ehcache Mybatis和EHCache的整合包
ehcache EHCache核心包
slf4j-api SLF4J 日志门面包
logback-classic 支持SLF4J门面接口的一个具体实
c>创建EHCache的配置文件ehcache.xml
<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\atguigu\ehcache"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
d>设置二级缓存的类型
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
e>加入logback日志
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。
创建logback的配置文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -
->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger]
[%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>
f>EHCache配置文件说明
![](file://D:\资料\学习笔记\Java\Mybatis\1.png?msec=1651885414653)
Mybatis逆向工程
Simple版本(只有增删改查五个功能)
正向工程: 先创建实体类,再由框架生成数据库表。Hibernate支持正向工程
逆向工程: 先创建数据库表,然后由框架生成下面的资源:
①实体类
②Mapper接口
③Mapper映射文件
a 添加依赖和插件
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
b 创建MyBatis核心文件
c 创建逆向工程的配置文件 generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mydb"
userId="root"
password="abc123">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.hikaru.mybatis.pojo"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.hikaru.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.hikaru.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
其中enableSubPackages属性声明是否使用子包(设为false的话,com.hikaru.mybatis.pojo就会被当成一整个路径名而不是多个)
trimStrings属性可以将数据库字段名前后的空格去掉,方便生成资源
d>执行MBG插件的generate目标
![](file://D:\资料\学习笔记\Java\Mybatis\2.png?msec=1651885414566)
测试
DEBUG 05-06 12:23:44,119 ==> Preparing: select eid, emp_name, age, sex, email, did from t_emp (BaseJdbcLogger.java:137)
DEBUG 05-06 12:23:44,143 ==> Parameters: (BaseJdbcLogger.java:137)
DEBUG 05-06 12:23:44,167 <== Total: 11 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='1A', age='23', sex='男', email='123@qq.com', did=1}
Emp{eid=2, empName='2B', age='22', sex='女', email='123@qq.com', did=2}
在执行过程中出现了数据库连接文件JBDC.properties以及log4j无法加载的问题,找了半天错,原来是target文件中没有这两个文件,在Maven中重新clean compile一下即可
高级版本(可实现条件CUID)
a 修改逆向工程核心配置文件的版本号
<context id="DB2Tables" targetRuntime="MyBatis3">
b 生成实体类Example,用于条件CURD 其余与Simple版本相同
c
测试查询、主键查询和条件查询
@Test
public void testGenerate() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.
SqlSessionFactory sqlFactory = new SqlSessionFactoryBuilder().b
SqlSession sqlSession = sqlFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
// List<Emp> emps = mapper.selectByExample(null);
// emps.forEach(emp -> System.out.println(emp));
EmpExample empExample = new EmpExample();
empExample.createCriteria().andAgeLike("%21%");
empExample.or().andEidEqualTo(3);
empExample.or().andEidEqualTo(4);
List<Emp> emps = mapper.selectByExample(empExample);
emps.forEach(emp -> System.out.println(emp));
}
输出SQL语句
Preparing: select eid, emp_name, age, sex, email, did from t_emp WHERE ( age like ? ) or( eid = ? ) or( eid = ? ) (BaseJdbcLogger.java:137)
Parameters: %21%(String), 3(Integer), 4(Integer) (BaseJdbcLogger.java:137)
Total: 2 (BaseJdbcLogger.java:137) 7)
测试两种修改、删除
@Test
public void testGenerate2() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
// (Integer eid, String empName, String age, String sex, String email, Integer did) {
// int result = mapper.updateByPrimaryKey(new Emp(1, "1A", null, "男", "456@qq.com", 2));
int result = mapper.updateByPrimaryKeySelective(new Emp(1, "1A", null, "男", "456@qq.com", 2));
System.out.println(result);
}
直接修改会将null也跟新到数据库字段中,而selective选择性修改碰到null则不会对数据库字段做任何修改
【Mybatis】(一)的更多相关文章
- 【分享】标准springMVC+mybatis项目maven搭建最精简教程
文章由来:公司有个实习同学需要做毕业设计,不会搭建环境,我就代劳了,顺便分享给刚入门的小伙伴,我是自学的JAVA,所以我懂的.... (大图直接观看显示很模糊,请在图片上点击右键然后在新窗口打开看) ...
- Java MyBatis 插入数据库返回主键
最近在搞一个电商系统中由于业务需求,需要在插入一条产品信息后返回产品Id,刚开始遇到一些坑,这里做下笔记,以防今后忘记. 类似下面这段代码一样获取插入后的主键 User user = new User ...
- [原创]mybatis中整合ehcache缓存框架的使用
mybatis整合ehcache缓存框架的使用 mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓 ...
- 【SSM框架】Spring + Springmvc + Mybatis 基本框架搭建集成教程
本文将讲解SSM框架的基本搭建集成,并有一个简单demo案例 说明:1.本文暂未使用maven集成,jar包需要手动导入. 2.本文为基础教程,大神切勿见笑. 3.如果对您学习有帮助,欢迎各种转载,注 ...
- mybatis plugins实现项目【全局】读写分离
在之前的文章中讲述过数据库主从同步和通过注解来为部分方法切换数据源实现读写分离 注解实现读写分离: http://www.cnblogs.com/xiaochangwei/p/4961807.html ...
- MyBatis基础入门--知识点总结
对原生态jdbc程序的问题总结 下面是一个传统的jdbc连接oracle数据库的标准代码: public static void main(String[] args) throws Exceptio ...
- Mybatis XML配置
Mybatis常用带有禁用缓存的XML配置 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ...
- MyBatis源码分析(一)开篇
源码学习的好处不用多说,Mybatis源码量少.逻辑简单,将写个系列文章来学习. SqlSession Mybatis的使用入口位于org.apache.ibatis.session包中的SqlSes ...
- (整理)MyBatis入门教程(一)
本文转载: http://www.cnblogs.com/hellokitty1/p/5216025.html#3591383 本人文笔不行,根据上面博客内容引导,自己整理了一些东西 首先给大家推荐几 ...
- MyBatis6:MyBatis集成Spring事物管理(下篇)
前言 前一篇文章<MyBatis5:MyBatis集成Spring事物管理(上篇)>复习了MyBatis的基本使用以及使用Spring管理MyBatis的事物的做法,本文的目的是在这个的基 ...
随机推荐
- Nexus系列---【使用docker搭建nexus3仓库】
1.Docker搭建nexus3私服 如果机器配置比较低,建议指定初始内存大小,默认2G docker run -d \ --restart=always \ --name=nexus3 \ -p 6 ...
- RabbitMQ管理控制台的使用
RabbitMQ管理控制台的使用 添加一个用户 添加一个Virtual Hosts 把虚拟机和用户绑定 点击创建的虚拟机 配置 rabbitmq.config cp rabbitmq.config.e ...
- Linux配置
JDK安装 一.登录虚拟机进入终端切换到root用户,输入:su 接着输入密码 再输入:cd - 回到root用户 二.接着在终端进入对应文件新建一个装jdk包的文件夹,输入:mkdir +目录名称 ...
- Training time_SSM
三阶段 MyBatis 1 三层架构介绍 2 MyBatis介绍 类库:对于现有技术的一个封装. 框架:对于一个问题的一整套解决方案. MyBatis是一个半自动的ORM持久层的框架.刚开始叫做iBa ...
- HDFS Property列表,适用于Hadoop 2.4以上 。
Property列表链接:http://hadoop.apache.org/docs/r2.4.1/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml 以 ...
- C# IDataReader转换为Json
1 /// <summary> 2 /// IDataReader转换为Json 3 /// </summary> 4 /// <param name="dat ...
- keshe
keshe 发送方a 代码: package org.example; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.ut ...
- NXOpen遍历工作部件表达式
//用户代码#include <uf_defs.h>#include <NXOpen/NXException.hxx>#include <NXOpen/Session.h ...
- windows音频设备图像隔离audiodg.exe占用内存高(停止与重启audiodg服务)
首先想到的办法是结束该进程,于是在任务管理器里结束进程后,内存是释放了,但是发现发现电脑没有声音去到电脑的system32目录下双击audiodg.exe后任然没有声音解决方法如下(重启audiodg ...
- Go_day04
Go基础语法 指针 指针式存储另一个变量内存地址的变量 &a 取出a的内存地址 *b 若指针b存放的式a的地址 那么 *b就直接指向a的内存 可以直接操作其中的值 指针的使用 func mai ...