【Java】Mybatis基础笔记

目录

0.框架架构

1.为什么要使用mybatis(jdbc的缺陷)

 2.和hibernate的区别

3.开发dao两种方法

3.1 原始dao开发方法

3.2 mapper代理方法(只需要mapper接口-->相当于dao接口)

4.#{}和${}

4.1 #{} 和 ${} 里面填的内容

4.2 #{} 和 ${} 在预编译中的处理是不一样的

5.SqlMapConfig.xml

5.1 配置内容

5.2 别名的设置

5.3 加载mapper映射文件

5.4读取属性的顺序

6.映射

6.1输入映射

6.2输出映射

7.动态sql

8.延迟加载

9.查询缓存

一级缓存

二级缓存

10.MyBatis整合其他框架

11.Mybatis和Spring的整合

11.1原始dao开发:

11.2mapper代理开发:

12.逆向工程

12.1 什么是逆向工程

12.2 使用


0.框架架构


SqlSessionFactoryBuilder(只需当成一个工具类,只用new一次,不用单例管理)

              ------->>>SqlSessionFactory(单例)

                     ------->>>SqlSession(线程不安全) 

 

1.为什么要使用mybatis(jdbc的缺陷)


  • 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
  • Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码
  • 使用preparedStatement向占位符传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
  • 结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。

 

 2.和hibernate的区别


  • mybatis不是一个完全的ORM框架,因为可以自己决定输出/输入映射,需要自己对SQL语句进行修改和优化

>>>>适用需求变化较多的项目:互联网项目

  • hibernate是标准的ORM框架,不需要写SQL,对SQL的优化和修改困难

>>>>适用需求变化不多的项目

 

3.开发dao两种方法


  • 3.1 原始dao开发方法

需要写dao接口dao实现类Statement的Xml

需要向dao实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建SqlSession

(通过SqlSession来调用xml中配置好的statement了)

 

*存在问题

  1. dao实现类有大量重复代码
  2. statement的id的硬编码 
  3. 调用SqlSession方法传入的变量,由于适用泛型,即使变量类型错误,在编译阶段也不报错

 

  • 3.2 mapper代理方法(只需要mapper接口-->相当于dao接口)

需要写mapper.java(相当于接口)mapper.xml

编写mapper.java需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象

开发规范:

  • 在mapper.xml中namespace等于mapper.java类地址
  • mapper.java的方法名和mapper.xml中statement的id一致
  • mapper.java方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致
  • mapper.java方法返回值类型和mapper.xml中statement的resultType指定的类型一致。

得到SqlSession后,再通过SqlSession.getMapper(...mapper.class)来获得mapper代理对象来调用xml中配置好的statement

 

4.#{}和${}


编写Statement的XML时用于表示输入变量的占位符(eg:SELECT * FROM ...WHERE username=#{username})

4.1 #{} 和 ${} 里面填的内容

如果输入的参数是简单类型,#{}中的参数名可以任意,可以使value或者其他什么

${}在传入简单类型时只能使用value

4.2 #{} 和 ${} 在预编译中的处理是不一样的

  • #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号,变成如下的 sql 语句:

username = 'username'

解析之后才会变成username = 'zhangsan'
  

  • $将传入的数据的值直接显示生成在sql中,变成如下

username = 'zhangsan'

解析后还是一样username = 'zhangsan'

所以优先使用 #{}。因为 ${} 会导致 sql 注入的问题,${}用于拼接sql


 

5.SqlMapConfig.xml


 

5.1 配置内容

properties(属性)

settings(全局配置参数)

typeAliases(类型别名)

typeHandlers(类型处理器)

objectFactory(对象工厂)

plugins(插件)

environments(环境集合属性对象)

environment(环境子属性对象)

transactionManager(事务管理)

dataSource(数据源)

mappers(映射器)

5.2 别名的设置

<typeAliases>
    <typeAlias type="...类路径..." alias="...所需变换的别名..."/>
</typeAliases>

 

5.3 加载mapper映射文件

<mappers>
    <mapper resource="...相对路径..."/>
</mappers>

*下面两种都要遵循规范,需要将mapper的接口类和mapper.xml映射文件名保持一致,且在同一个目录中

<mappers>
    
    <!-- 单个导入 -->
    <mapper class="...包路径...">
    <!-- 批量导入 -->
    <package name="...包路径..."/>

</mappers>

 

5.4读取属性的顺序

<properties resource="db.properties" url="">
    <property name="" value=""/>
</properties>
  • 在property元素体内定义的属性首先被读取
  • 然后读取properties元素中resource或url加载的属性,会覆盖已读的属性值
  • 最后读取parameterType传递的属性,会覆盖已读取的同名属性

parameterType是输入映射要传入的参数,传入的参数值也会覆盖原有的相同名字的值

 

6.映射


  • 6.1输入映射

传入pojo的包装对象

传入简单类型

传入hashmap

  • 6.2输出映射

*resultType:

使用resultType进行输出映射,查询出来的列名要和pojo中的属性名一致(有可能要自己创一个pojo类),才能映射成功

如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。

只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。

 

*resultMap:

而使用resultMap,就可以解决列名和属性名不一致导致不能映射成功的问题

使用:现在Mapper.xml定义resultMap标签

eg: *将查询出来的列名id__映射到user对象的属性id,将查询出来的列名username__映射到user对象的属性username

<resultMap type="com.bean.user" id="resultMap">
    <id column="id__" property="id"/>
    <result column="username__" property="username"/>
</resultMap>

>>>type:resultMap要映射的java对象类型

>>>id-column:得到的列名 

>>>id-property:要对应的javabean的属性名

*resultType和resultMap区别:

①resultType输出的字段名要和输出映射的pojo类的属性名一致

②在一对多的时候,使用resultType会产生重复字段问题

eg:查出来一个订单对应N个订单明细信息,就会显示成N条记录,但我们需要只是一条订单记录,记录里面有N条订单明细信息

而如果要解决,就要使用双重循环(第一层循环订单信息,第二层循环对应订单的订单明细信息)

如果用resultMap就在配置resultMap的时候

通过<collection></collection>标签来自动去重,原理也是双重for循环,只不过系统帮我们解决了

所以在一对多以及多对多特殊情况时

还是使用resultMap

单表查询的时候就使用resultType

7.动态sql


差不多同jstl一样

eg:

<select ...>

SELECT * FROM USER

<!-- 用where标签可以自动省掉去掉条件中第一个and,或者直接用1=1来拼接也可以 -->
<where>
    <if test="userNew!=null">
        <if test="userNew.username!=null and userNew.username!=''">
            and user.username = #{userNew.username}
        </if>
        <if test="userNew.password!=null and userNew.password!=''">
            and user.password= #{userNew.password}
        </if>
    </if>
</where>

</select>

对于可重用的sql,可用sql片段来重用

>>定义:<sql id="hello">...sql...</sql>

>>使用:<include refid="hello"></include>

需要循环加sql的可以用foreach标签

<foreach collection="" item="" open="" close="" separator="">..sql...</foreach>

>>collection:传入对象中的集合属性

>>item:遍历生成的每一个对象

>>open:以什么sql作为开始拼接的信号

>>close:以什么sql作为结束拼接的信号

>>separator:每加入一个sql之间的分隔字符串

 

8.延迟加载


先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度快

要在SqlMapConfig

<settings>
    <!-- 打开延迟加载的开关 -->
    <setting name="lazyLoadingEnabled" value="ture"/>
     <!-- 将积极加载改为消极加载==按需要加载 -->
    <setting name="aggressiveLazyLoading" value="false"/>

</settings>

只能使用resultMap来实现延迟加载

配置resultMap时使用标签<association property="" javatype="" select="" column=""></association>

>>>property:将查到的对象映射到哪个类属性

>>>javatype:需要延迟加载的表的类路径

>>>select:指定延迟加载需要执行的statement的id

>>>column:两张表之间关联的那个键名

<association></association>是用来配置输出映射中类的属性为另一个类(比如User类里面有Customer类这个属性)

如果不需要延迟加载,可以不用后面两个属性(select / column)

9.查询缓存


  • 一级缓存

用同一个sqlSession执行同一个mapper中的select方法时,可以从缓存中提取数据

清空缓存的情况:1.sqlSession.close()  2.sqlSession.commit()

  • 二级缓存

每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中

开启二级缓存

SqlMapConfig:

<settings>
    ...
    <setting name="cacheEnabled" value="true"/>
    ...
</settings>

需要开启二级缓存的Mapper.xml(type属性是用来整合其他缓存框架[ehcache-->集群缓存],写实现cache实现类的类路径)

<mapper namespace="...">
    ...
    <cache type=""/>
    ...
</mapper

useCache="false" --> 用在select标签中 可以禁用二级缓存

flushCache="false" --> 用在insert标签中可以导致脏读,即改变数据库数据后也不刷新缓存

局限性

简单来说,就是已知二级缓存里面已经存了1K个数据,但是只要其中一个SqlSession提交了数据(commit)这1K个数据全部都清空了,就导致缓存的命中率很低

官方来说,mybatis二级缓存对细粒度的数据级别的缓存实现不好,解决此类问题需要在业务层根据需求对数据有针对性缓存

10.MyBatis整合其他框架


MyBatis提供了一个Cache接口,如果要实现自己的缓存逻辑,只需要实现该接口即可

用Mybatis和ehcache整合,在Mybatis和ehcache的整合包中提供了一个cache接口的实现类

Mybatis默认的cache的实现类:

只需在mapper.xml中配置cache标签的type属性,以及ehcache的配置文件即可完成配置

11.Mybatis和Spring的整合


导包

配置SqlSessionFactory(SqlMapConfig和dataSource)

和dataSource(driverCalssName/url/username/password/...)

两种dao的开发方法:

11.1原始dao开发:

1.User.xml 写Statement

2.写dao

3.写dao的实现类(调用selectOne或selectList(namespace.select.id))

*让dao实现类继承SqlSessionDaoSupport(已有属性SqlSessionFactory)

4.在Spring的配置文件中配置dao实现类的SqlSessionFactory

11.2mapper代理开发:

~1.mapper.xml和mapper.java(就是dao)

~2.0.通过mapperFactoryBean来创建对象(一个mapper需要一个FactoryBean来创建对象)

~2.1.通过MapperScannerConfigurer进行mapper扫描(mapper的批量扫描)

两个都需要注入sqlSessionFactory(第二种方法要注入sqlSessionFactoryBeanName,先后执行问题)

12.逆向工程


 

12.1 什么是逆向工程

由数据库的表生成java代码,以针对单表自动生成mybatis执行所需要的代码(mapper.java,mapper.xml、pojo等)

12.2 使用

生成代码配置文件

<?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>
	<context id="testTables" targetRuntime="MyBatis3">
		<commentGenerator>
			<!-- 是否去除自动生成的注释 true:是 : false:否 -->
			<property name="suppressAllComments" value="true" />
		</commentGenerator>
		<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
		<jdbcConnection driverClass="com.mysql.jdbc.Driver"
			connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
			password="mysql">
		</jdbcConnection>
		<!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
			connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
			userId="yycg"
			password="yycg">
		</jdbcConnection> -->

		<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 
			NUMERIC 类型解析为java.math.BigDecimal -->
		<javaTypeResolver>
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>

		<!-- targetProject:生成PO类的位置 -->
		<javaModelGenerator targetPackage="cn.itcast.ssm.po"
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
			<!-- 从数据库返回的值被清理前后的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
		<sqlMapGenerator targetPackage="cn.itcast.ssm.mapper" 
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>
		<!-- targetPackage:mapper接口生成的位置 -->
		<javaClientGenerator type="XMLMAPPER"
			targetPackage="cn.itcast.ssm.mapper" 
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</javaClientGenerator>
		<!-- 指定数据库表 -->
		<table tableName="items"></table>
		<table tableName="orders"></table>
		<table tableName="orderdetail"></table>
		<table tableName="user"></table>

		
	</context>
</generatorConfiguration>

执行生成程序

猜你喜欢

转载自blog.csdn.net/TypantK/article/details/83994575