Mybatis3

Mybatis

作用域和生命周期

  1. SqlSessionFactoryBuilder

最佳作用域是方法作用域,一旦创建了SqlSessionFactory就不再需要它了。可以重用SqlSessionFactoryBuilder来创建多个SqlSessionFactory实例,但是不要一直保留SqlSessionFactoryBuilder。

  1. SqlSessionFactory

一旦被创建就应在应用运行期间一直存在,避免多次创建SqlSessionFactory,作用域是应用作用域。

  1. SqlSession

每个线程都有它自己的SqlSession实例,不是线程安全的,不能共享,最佳的作用域是请求域或者方法域。

//标准用法
try(SqlSession session = sqlSessionFactory.openSession()){
}
  1. 映射器实例

绑定一些映射语句的接口,映射器的接口实例是从SqlSession中获得的。最好的作用于就是方法作用域。

try(SqlSession session = sqlSessionFactory.openSession()){
BlogMapper mapper = session.getMapper(BlogMapper.class);
}

Mabatis配置文件的顶层结构

image-20221213195056168

image-20221214142956082

SqlSessionFactoryBuilder拿着配置文件去创建SqlSessionFactory

属性(Properties)

<properties resource="org/mybatis/example/config.properties">
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>

设置好的属性可以在整个配置文件中替换需要动态配置的值。其中${}里面表达式的值可以通过上面的Properties标签设置的值也可以从config.properties文件中读取相应的配置信息

<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>

默认属性(MyBatis >= 3.4.2 )

<properties resource="org/mybatis/example/config.properties">
<!-- ... -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 -->
<property name="username" value="${username:ut_user}"/> <!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
</properties>

如果属性名中包含':'那么按如下设置

<properties resource="org/mybatis/example/config.properties">
<!-- ... -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 -->
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- 修改默认值的分隔符 -->
</properties>
<property name="username" value="${db:username?:ut_user}"/>

设置(Setting)

懒得写了( •̀ ω •́ )✧

类型别名(typeAliases)

<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

image-20221213202907488

类型处理器(typeHandlers)

MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。

各种类型处理器🌈

可以从写已有的类型处理器或者创建自己的类型处理器。

  • 实现org.apache.ibatis.type.TypeHandler接口
  • 继承org.apache.ibatis.type.BaseTypeHandler
package cn.pickle;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author Pickle
* @version V1.0
* @date 2022/12/13 20:34
*/
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i, s);
}
@Override
public String getNullableResult(ResultSet resultSet, String s) throws SQLException {
return resultSet.getString(s);
}
@Override
public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
return resultSet.getString(i);
}
@Override
public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return callableStatement.getString(i);
}
}
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>

使用上述的类型处理器将会覆盖已有的处理 Java String 类型的属性以及 VARCHAR 类型的参数和结果的类型处理器。

处理枚举类型

image-20221214104534953

定义枚举处理器,全局适用

<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</typeHandlers>

自动映射器(auto-mapper)会自动地选用 EnumOrdinalTypeHandler 来处理枚举类型, 所以如果我们想用普通的 EnumTypeHandler,就必须要显式地为那些 SQL 语句设置要使用的类型处理器。

image-20221214105650690

对象工厂

Mybatis在创建结构对象的新实例的时候会用ObjectFactory实例来完成实例化工作。可以扩展DefaultObjectFactory来自定义对象工厂。

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
@Override
public <T> T create(Class<T> type) {
return super.create(type);
}
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
}
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
}
@Override
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}}
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>

ObjectFactory 接口很简单,它包含两个创建实例用的方法,一个是处理默认无参构造方法的,另外一个是处理带参数的构造方法的。 另外,setProperties 方法可以被用来配置 ObjectFactory,在初始化你的 ObjectFactory 实例后, objectFactory 元素体中定义的属性会被传递给 setProperties 方法。

插件

💩

环境配置(Environment)

每个SqlsessionFactory实例只能选择一种环境

<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
  • 默认使用的环境ID(比如:defalut="development")
  • 每个environment元素定义的环境ID(比如:id="development")
  • 事务管理器的配置(比如:type="JDBC")
  • 数据源的配置(比如:type="POOLED")

事务管理器

  • JDBC

使用JDBC的提交和回滚

  • MANAGED

不做任何事情

数据源

  • UNPOOLED

每次请求打开和关闭连接

  • POOLED

利用池化思想将JDBC连接对象组织起来,避免创建新的连接是所需要的初始化和认证时间。

  • JNDI

XML映射器

image-20221214134038833

Select

Select 元素属性

<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">

image-20221214150812922

<select id="selectStudent" resultType="student">
Select * from student where id = #{id};
</select>

{id}告诉Mybatis创建一个预处理语句

// 近似的 JDBC 代码,非 MyBatis 代码...
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);

在Intert操作中设置主键

<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>
<selectKey
keyProperty="id"
resultType="int"
order="BEFORE"
statementType="PREPARED">

image-20221214152512498

在Insert的时候需要在可能为空的列上加上jdbcType。

image-20221214154621042

字符串替换

image-20221215100401927

image-20221215100538938

用这种方式接受用户的输入,并用作语句参数是不安全的,会导致潜在的 SQL 注入攻击。因此,要么不允许用户输入这些字段,要么自行转义并检验这些参数。

结果映射

对简单的语句做到零配置,对复杂的语句,只需要描述语句之间的关系就行了。

<!-- mybatis-config.xml 中 -->
<typeAlias type="com.someapp.model.User" alias="User"/>
<!-- SQL 映射 XML 中 -->
<select id="selectUsers" resultType="User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>

Mybatis会利用一个ResultMap根据属性名来映射到JavaBean上,如果列明和属性名不匹配,可以在Select语句中设置别名。

<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password as "hashedPassword"
from some_table
where id = #{id}
</select>

使用ResultMap

<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>

Id&result

id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。

  • properties

映射到结果的字段或属性,先去寻找properties同名的属性,再去寻找给定名称的字段field(成员变量)。

  • column

数据库列名或者列的别名。

  • javaType

一个Java类的全限定名

  • jdbcType

只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。

  • typeHandler

类型处理器。

构造方法

😐

image-20221215132833232

关联的嵌套Select查询

<resultMap id="blogResult" type="Blog">
<association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
</select>
<select id="selectAuthor" resultType="Author">
SELECT * FROM AUTHOR WHERE ID = #{id}
</select>

我们有两个 select 查询语句:一个用来加载博客(Blog),另外一个用来加载作者(Author),而且博客的结果映射描述了应该使用 selectAuthor 语句加载它的 author 属性。
其它所有的属性将会被自动加载,只要它们的列名和属性名相匹配。

缓存

在Sql映射文件中加入<cache/>

image-20221215200440461

缓存只存在于 <cache/>标签所在的映射文件的语句

在使用缓存时entity类需要实现Serializable接口否则就会报错

image-20221216172203989

Sql语句构造器

从版本 3.4.2 开始,你可以像下面这样使用可变长度参数:

public String selectPersonSql() {
return new SQL()
.SELECT("P.ID", "A.USERNAME", "A.PASSWORD", "P.FULL_NAME", "D.DEPARTMENT_NAME", "C.COMPANY_NAME")
.FROM("PERSON P", "ACCOUNT A")
.INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID", "COMPANY C on D.COMPANY_ID = C.ID")
.WHERE("P.ID = A.ID", "P.FULL_NAME like #{name}")
.ORDER_BY("P.ID", "P.FULL_NAME")
.toString();
}
public String insertPersonSql() {
return new SQL()
.INSERT_INTO("PERSON")
.INTO_COLUMNS("ID", "FULL_NAME")
.INTO_VALUES("#{id}", "#{fullName}")
.toString();
}
public String updatePersonSql() {
return new SQL()
.UPDATE("PERSON")
.SET("FULL_NAME = #{fullName}", "DATE_OF_BIRTH = #{dateOfBirth}")
.WHERE("ID = #{id}")
.toString();
}

3.5.2开始可以批量插入

public String insertPersoinSql(){
//INSERT INTO PERSON(ID,FULL_NAME)
// VALUES(#{mainPerson.id},#{mainPerson.fullName}),(#{sunPerson.id},#{subPerson.fullName})
return new SQL()
.INSERT_INTO("PERSON")
.INTO_COLUMS("ID","FULL_NAME")
.INTO_VALUES("#{mainPerson.id}","#{mainPerson.fullName}")
.ADD_ROW()
.INTO_VALUES("#{subPerson.id}","#{subPerson.fullName}")
.toString();
}

日志

要在Mybatis中使用日志,需要在 mybatis-config.xml<setting/>标签中配置

image-20221216164107017

  • STDOUT_LOGGING 标准日志
  • LOG4J Java日志(Log for Java)
  • LOG4J2 船新版本
  • SLF4J 简单日志门面 (Simple Logging for Java)

未配置日志前的查询输出

image-20221216164804324

STDOUT_LOGGING

<settings>
<!-- 标准日志工厂实现 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

配置了日志的输出

image-20221216165453999

按照图中标出的数据

  • Opening JDBC ---- 打开Jdbc连接
  • Created connection ---- 创建连接

其中箭头指出的显示将autocommit to false是因为我们在配置文件中配置事务管理的时候设置为true

image-20221216165812405

  • 之后就是查询的Sql语句和返回的结果

Log4J

  • Log4j 是 Apache 的一个开源项目,通过使用 Log4j ,可以控制日志信息输送的目的地是控制台、文件、GUI 组件、甚至是套接口服务器、NT 的事件记录器、UNIX Syslog 守护进程等
  • 也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,能够更加细致地控制日志的生成过程
  • 这些都可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码

导入Maven坐标

<!-- log4j 日志依赖 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

配置文件

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/qiyuan.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

mybatis-config.xml

<setting>
<!--使用 log4j 注意不能打错-->
<setting name="logImpl" value="LOG4J"/>
</setting>

查询后的输出

image-20221216170955548

可以看到在Reader entry的时候出现了乱码,这是因为我们用了<typeAliases>设置类别名导致的,把使用别名的地方改一下,就不会出现乱码了。

注释<typeAliases></typeAliases>,

image-20221216171352639

Mapper文件中改为全限定名

image-20221216171436621

再次查询,乱码不见了

image-20221216171549506

posted @   破忒头头  阅读(71)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示