Mybatis学习笔记02
1. 配置解析
1.1 核心配置文件(mybatis-config.xml)
- mybatis-config.xml
- Mybatis的配置文件包含了会深深影响Mybatis行为和设置的属性信息
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
1.2 环境配置(environments)
Mybatis可以配置成适应多种环境
注意:尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境
1.3 属性(properties)
我们可以通过properties属性来实现引用配置文件(db.properties)
这些属性都是可外部配置且可动态替换的,既可以在典型的java属性文件中配置,也可通过properties元素的子元素来传递
在xml中,所有的标签都有规定的顺序
编写一个配置文件
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
username=root
password=321074
在核心配置文件中引入
<!-- 在核心配置文件中引入-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="321074"/>
</properties>
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两一个文件有同一个字段,优先使用外部配置文件的
1.4 类型别名(typeAliases)
类型别名是为java类型设置一个短的名字
存在的意义仅在于用于类完全限定名的冗余
<typeAliases>
<typeAlias type="com.jcsune.pojo.User" alias="User"/>
</typeAliases>
也可以指定一个包名,Mybatis会在包名下面搜索需要的Java Bean,比如:
扫描实体类的包,它的默认别名就为这个类的类名,首字母小写
<typeAliases>
<package name="com.jcsune.pojo"/>
</typeAliases>
在实体类比较少的时候,使用第一种方式
如果实体类比较多,建议使用第二种
第一种可以diy别名,第二种不行,如果非要改,需要在类上面添加注解
@Alias("user")
public class User {}
1.5 设置
这是Mybatis极为重要的调整设置,它们会改变mybatis的运行时行为
1.6 其它配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins插件(mybatis-plus,通用mapper,mybatis-generator-core)
1.7 映射器
MapperRegistry:注册绑定我们的Mapper文件
方式一:推荐使用
<!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册!-->
<mappers>
<mapper resource="com/jcsune/dao/UserMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册
<!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册!-->
<mappers>
<mapper class="com.jcsune.dao.UserMapper.xml"/>
</mappers>
注意点:
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一个包下
方式三: 使用扫描包进行注入绑定
<!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册!-->
<mappers>
<package name="com.jcsune.dao"/>
</mappers>
1.8 生命周期和作用域
生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题
SqlSessionFactoryBuilder:
- 一旦创建了SqlSessionFactory,就不在需要它了
- 局部变量
SqlSessionFactory
- 可以理解为数据库连接池
- SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
- SqlSessionFactory的最佳作用域是应用作用域
- 最简单的就是使用单例模式或者静态单例模式
SqlSession
- 连接到连接池的一个请求
- SqlSession的实例不是线程安全的,因此是不能被共享的,所以他的最佳作用域是请求或方法作用域
- 用完之后需要赶紧关闭,否则资源被占用
这里面的每一个Mapper,就代表一个具体的业务
2. 解决属性名和字段名不一致的问题
2.1 问题
数据库中的字段:
新建一个项目,拷贝之前的,测试实体类字段不一致的情况
public class User {
private int id;
private String name;
private String password;
....
}
测试出现问题:
解决方法一:起别名
<select id="getUserList" resultType="User">
select id,name,pwd as password from mybatis.user
</select>
解决方法二:结果集映射,见下一节介绍
2.2 resultmap(结果集映射)
<!-- 结果集映射-->
<resultMap id="UserMap" type="User">
<!-- column: 数据库中的字段 property:实体类中的属性-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<!--select查询语句-->
<select id="getUserList" resultMap="UserMap">
select * from mybatis.user
</select>
- resultMap元素是Mybatis中最重要最强大的元素
- resultMap的设计思想是对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述他们的关系就行了
- resultMap最优秀的地方在于,虽然你已经对他相当了解了,但是根本就不需要显式的用到他们
3. 日志
3.1 日志工厂
如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手
曾经:sout、debug
现在:日志工厂
需要掌握的是 LOG4J 、STDOUT_LOGGING
在Mybatis中具体使用哪一个日志实现,在设置中设定
STDOUT_LOGGING 标准日志输出
在Mybatis核心配置文件中,配置我们的日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
输出结果:
3.2 Log4j
- Log4j是Apache的一个开源项目,通过使用Log4j, 我们可以控制日志信息输送的目的地是控制台,文件,GUI组件
- 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致的控制日志的生成过程
- 通过一个配置文件来灵活的进行配置,则不需要修改应用的代码
1.先导入log4j的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.log4j.properties
#将等级为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/kuang.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
3.配置log4j为日志的实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4.直接测试运行:
简单使用:
- 在要使用Log4j的类中,导入包 import org.apache.log4j.Logger;
- 日志对象:参数为当前类的class
static Logger logger=Logger.getLogger(UserDaoTest.class);
- 日志级别
logger.info("info:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
logger.error("error:进入了testLog4j");
- 结果
4. 分页
为什么要分页?
答:减少数据的处理量
4.1 使用Limit分页
Sql语法:
select * from user limit startIndex,pageSize;
select * from user limit n; #[0,n]
使用Mybatis实现分页,核心sql
- 接口
//分页
List<User> getUserByLimit(Map<String,Integer> map);
- Mapper.xml
<!-- 分页-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
- 测试
@Test
public void getUserByLimit() {
//第一步,获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String,Integer> map=new HashMap<String, Integer>();
map.put("startIndex",1);
map.put("pageSize",2);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
- 结果
4.2 RowBounds分页
不再使用SQL进行分页
- 接口
//分页2
List<User> getUserByRowBounds();
- mapper.xml
<!-- 分页2-->
<select id="getUserByRowBounds" resultMap="UserMap">
select * from mybatis.user
</select>
- 测试
@Test
public void getUserByRowBounds() {
//第一步,获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds实现
RowBounds rowBounds=new RowBounds(1,2);
//通过java代码层面实现分页
List<User> userList = sqlSession.selectList("com.jcsune.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
- 结果
4.3 分页插件
url: https://pagehelper.github.io/
了解即可!
5. 使用注解开发
5.1 面向接口编程
之前学过面向对象编程,也学习过接口,但在真正的开发中,很多时候会选择面向接口编程
根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵从共同的标准,使得开发变得容易,规范性更好
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的,在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了
而面向对象之间的协作关系则成为系统设计的关键,小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容,面向接口编程就是指按照这种思想来编程
关于接口的理解:
- 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离
- 接口的本身反映了系统设计人员对系统的抽象理解
- 接口应有两类:
- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class)
- 第二类是对一个个体某一个方面的抽象,即形成一个抽象面(interface)
- 一个体有可能有多个抽象面,抽象体和抽象面是有区别的
三个面向区别:
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性和方法
- 面向过程是指,我们在考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构
5.2 使用过注解开发
- 注解在接口上实现
public interface UserMapper {
//查询
@Select("select * from user")
List<User> getUserList();
}
- 需要在核心配置文件中绑定接口
<!-- 绑定接口-->
<mappers>
<mapper class="com.jcsune.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();
}
- 结果
本质:反射机制实现
底层:动态代理
5.3 CRUD
可以在工具类创建的时候实现自动提交事务
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
编写接口,增加注解
public interface UserMapper {
//查询
@Select("select * from user")
List<User> getUserList();
// 方法存在多个参数,所有的参数前面必须加上 @Param("id")注解
@Select("select * from user where id=#{id} ")
User getUserById(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
int addUser(User user);
@Update("update user set name=#{name},pwd=#{password} where id = #{id}")
int updateUser(User user);
@Delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);
}
测试类:
注意:我们必须要将接口注册绑定到我们的核心配置文件中
关于@Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议加上
- 在SQL中引用的就是@Param()中设定的属性名
6.Lombok
开发项目过程中,会有很多的 pojo. pojo 又叫做 javabean,bean,entity 等等,都是他。pojo会有很多的 setter 和 getter , toString, hashcode, equals 等等
为了偷懒,我们就可以用 lombok. 用了之后就会如下代码所示,加上注解就行了
使用步骤:
- 在IDEA中安装Lombok插件
- 在项目中导入lombok的jar包
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
- 在实体类上加注解即可
@Data
@AllArgsConstructor
@NoArgsConstructor
@Data 注解会为类的所有属性自动生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
@AllArgsConstructor 、@NoArgsConstructor分别提供全参构造方法和无参构造方法