Spring 持久化笔记(JdbcTemplate、Mybatis、Redis)

JdbcTemplate类

JdbcTemplate类是Spring框架数据抽象层的基础,其他更高层次的抽象类是构建于JdbcTemplate类之上的,JdbcTemplate类是Spring JDBC的核心类,使用方法很简单,可以直接执行sql语句。

  • execute:可以执行所有SQL语句,一般用于执行DDL语句。
  • update:用于执行INSERT、UPDATE、DELETE等DML语句
  @Override
  public int deleteUser(int id) {
    
    
    String sql = "delete from  user1 where id = ?";
    int num = jdbcTemplate.update(sql, id);
    System.out.println(num);
    return 1;
  }
  
// queryForInt
String sql = "SELECT pid FROM product WHERE price=18888;";
JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
int forInt = jdbcTemplate.queryForInt(sql);

// 查询一个用户 
public User findUserById(int id) {
    
    
   System.out.println(id);
   String sql = "select * from user1 where id = ?";
   RowMapper<User> maps = new BeanPropertyRowMapper<>(User.class);
   User user =  jdbcTemplate.queryForObject(sql, maps, id);
   System.out.println(user);
   return user;
 }
  
// 查询列表
public List<User> findAll() {
    
    
  String sql = "select * from user1";
  RowMapper<User> maps = new BeanPropertyRowMapper<>(User.class);
  return  jdbcTemplate.query(sql, maps);
}

Spring事务管理

Spring的事务管理简化了传统的事务管理流程,主要介绍以下方面。

PlatformTransactionManager接口

  • 它是平台事务管理器,有3个方法
TransactionStatus getTransaction(TransactionDefinition definition):
用于获取事务状态信息。该方法会根据TransactionDefinition参数返回一个TransactionStatus对象。TransactionStatus对象表示一个事务,被关联在当前执行的线程上。 
void commit(TransactionStatus status):用于提交事务。 
void rollback(TransactionStatus status):用于回滚事务。
  • PlatformTransactionManager接口只是代表事务管理的接口,并不知道底层是如何管理事务的
  • 当底层采用不同的持久层技术时,系统只需使用不同的PlatformTransactionManager实现类即可

TransactionDefinition接口

  • 是事务定义(描述)的对象,该对象中定义了事务规则,提供了获取事务信息的方法如下
  • 事务的传播行为是指在同一个方法中,不同操作前后所使用的事务,在事务管理过程中,传播行为可以控制是否需要创建事务以及如何创建事务
  • 修改数据的操作必须进行事务管理,如果没有指定事务的传播行为,Spring默认传播行为是REQUIRED
 string getName():获取事务对象名称。 
 int getlsolationLeve():获取事务的隔离级别。
 int getPropagationBehavior():获取事务的传播行为。 
 int setTimeout():获取事务的超时时间。
 boolean isReadOnly():获取事务是否只读。

事务的传播行为分类

Name 介绍
Propagation.REQUIRED 如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务
Propagation.SUPPORTS 如果当前存在事务,则加入该事务,如果当前不存在事务,则以非事务的方式继续运行
Propagation.MANDATORY 如果当前存在事务,则加入该事务,如果当前不存在事务,则抛出异常
Propagation.REQUIRES_NEW 重新创建一个新的事务,如果当前存在事务,延缓当前的事务
Propagation.NOT_SUPPORTED 以非事务的方式运行,如果当前存在事务,暂停当前的事务
Propagation.NEVER 以非事务的方式运行,如果当前存在事务,则抛出异常
Propagation.NESTED 如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务

TransactionStatus接口

是事务的状态,描述了某一时间点上事务的状态信息

void flush():刷新事务。 
boolean hasSavepoint():获取是否存在保存点。
boolean isCompleted():获取事务是否完成。
boolean isNewTransaction():获取是否是新事务。
boolean isRollbackOnly():获取是否回滚。
void setRollbackOnly():设置事务回滚

Spring中的事务管理

编程序事务管理:通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚

声明式事务管理:就是通过AOP实现的事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP将事务管理的“切面”代码植入业务目标类中,常用这种。

声明式事务管理使用

和其他技术一样,有XML方法和注解方式两种使用方法。
XML配置如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">


    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/hua?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT&amp;useSSL=true" />
        <property name="username" value="root" />
        <property name="password" value="123" />
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" />

    <bean id="userDao" class="com.starbooks.tmail.data.JdbcUserRepository">
        <constructor-arg ref="jdbcTemplate" name="jdbcTemplate"/>
    </bean>

    <!-- 事务管理器,依赖数据源-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 通知,对事务进行增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="txPoint1" expression="execution(* com.starbooks.tmail.data.JdbcUserRepository.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint1" />
    </aop:config>
</beans>

使用注解
只需要使用 @Transactional 修饰类或者方法即可

Mybatis

  • 是当前主流的Java持久层框架之一,与Hibernate一样,也是一种ORM(ORM(Object/ Relational Mapping,对象关系映射 )框架。
  • 支持普通SQL查询、存储过程以及高级映射,并使用简单的XML或注解进行配置和原始映射,用以将接口和Java的POJO(Plain Old Java Object,普通Java对象)映射成数据库中的记录。
  • 使用ORM框架后,应用程序不再直接访问底层数据库,而是以面向对象的方式来操作持久化对象(Persisent Object,PO),而ORM框架则会通过映射关系将这些面向对象的操作转换成底层的SQL操作

Hibernate和Mybatis比较

  • Hibernate:全表映射的框架。通常只需定义好持久化对象到数据库表的映射关系,就可通过Hibernate提供的方法完成持久层操作。不需熟练掌握SQL语句编写,Hibernate会根据制定的存储逻辑自动生成SQL,并调用JDBC接口执行,其开发效率会高于MyBatis。但Hibernate自身存在着一些缺点,例如它在多表关联时,对SQL查询的支持较差;更新数据时,需要发送所有字段、不支持存储过程不能通过优化SQL来优化性能等。这些问题导致其只适合在场景不太复杂且对性能要求不高的项目中使用。

  • MyBatis:半自动映射框架。这里的“半自动”是相对于Hibernate全表映射而言的,MyBatis需要手动匹配提供POJO、SQL和映射关系,而Hibernate只需提供POJO和映射关系即可。与Hibernate相比,虽然使用MyBatis手动编写SQL要比使用Hibernate的工作量大,但MyBatis可以配置动态SQL并优化SQL,可以通过配置决定SQL的映射规则,它还支持存储过程等。对于一些复杂的和需要优化性能的项目来说,显然使用MyBatis更加合适。

基本使用

1 我的项目是spring boot,所以引入依赖
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>2.1.0</version>
</dependency>

 2 在resources下新建 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="mysql">
        <!--environment 元素体中包含了事务管理和连接池的配置    -->
        <environment id="mysql">

            <!--使用jdbc管理事务-->
            <transactionManager type="JDBC" />

            <!--数据库连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT&amp;useSSL=true"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>
</configuration>

3 同样在resource 目录下,新建 mapper文件 mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="UserMapper1">
    <select id="findUserById"
            parameterType="Integer"
            resultType="com.starbooks.tmail.pojo.User">
        select * from user1 where id = ${
    
    id}
    </select>
    <select id="findUserByName"
            parameterType="String"
            resultType="com.starbooks.tmail.pojo.User">
        select * from user1 where name like '%${value}%'
    </select>

</mapper>

4 测试使用
public class MybatisTest {
    
    

  @Test
  public void findUser() throws IOException {
    
    
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    User user = sqlSession.selectOne("UserMapper1.findUserById", 1);
    List<User> users = sqlSession.selectList("UserMapper1.findUserByName", "hua");
    System.out.println(user);
    System.out.println(users);
    sqlSession.close();
  }
}

SqlSessionFactory

  • SqlSessionFactory是单个数据库映射关系经过编译后的内存镜像,用于创建SqlSession
  • SqlSessionFactory对象是线程安全的,一旦被创建,在整个应用执行期间都会存在
  • 所以在构建SqlSessionFactory实例时,建议使用单列模式。

SqlSession

  • 是应用程序与持久层之间执行交互操作的一个单线程对象,其主要作用是执行持久化操作
  • SqlSession对象包含数据库中所有执行SQL操作的方法,底层封装了JDBC连接,所以可以直接使用其实例来执行已映射的SQL语句
  • SqlSession实例是不能被共享的、线程不安全的,其使用范围最好限定在一次请求或一个方法中

MyBatis配置文件元素

properties
配置属性的元素,通过外部配置来动态替换内部定义的属性

// 可以使用外边文件的变量 
<properties resource="application.properties" />

settings
用于改变MyBatis运行时的行为,例如开启二级缓存、开启延迟加载等

typeAliases
为配置文件中的Java类型设置别名

<typeAliases>
    <typeAlias type="com.starbooks.tmail.pojo.User" alias="user"/>
</typeAliases>
// 还可以添加一个包,自动识别 
<typeAliases>
   <package name="com.starbooks.tmail.pojo"/>
</typeAliases>
// 还可以手动给类加一个alias
@Alias("user")
public class User {
    
    

// Mybatis默认类名
_int int 
map Map 

typeHandler

MyBatis在预处理语句(Prepared Statement)中设置一个参数或者从结果集(Resultset)中取出一个值时,都会用其框架内部注册了的typeHandler(类型处理器)进行相关处理。typeHandler的作用就是将预处理语句中传入的参数从javaType(Java类型)转换为jdbcType(JDBC类型),或者从数据库取出结果时将jdbcType转换为javaType,例如

BooleanTypeHandler java.lang.Boolean 

当这些不够用时,自定义类型处理器可以通过实现TypeHandler接口或者继承BaseTypeHandle类来定义

 <typeHandlers>-->
      <typeHandler handler="com.starbooks.tmail.handlers" />
 </typeHandlers>

<plugins>
允许在已映射语句执行过程中的某一点进行拦截调用(通过插件来实现)。<plugins>元素的作用就是配置用户所开发的插件

environment
在MyBatis中,可以配置两种类型的事务管理器,分别是JDBC和MANAGED
如果项目中使用Spring+MyBatis,就没有必要在MyBatis中配置事务管理器,因为实际开发中会使用Spring自带的管理器来实现事务管理

environment datasource
数据源的配置,MyBatis框架提供了UNPOOLED、POOLED和JNDI三种数据源类型。 UNPOOLED:配置此数据源类型后,在每次被请求时会打开和关闭连接。它对没有性能要求的简单应用程序是一个很好的选择。 
POOLED:此数据源利用“池”的概念将JDBC连接对象组织起来,避免在创建新的连接实例时需要初始化和认证的时间。这种方式使得并发Web应用可以快速地响应请求,是当前流行的处理方式。 
JNDI:此数据源可以在EJB或应用服务器等容器中使用。容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用

mappers

 <mappers>
     <mapper resource="mapper/UserMapper.xml" />
     <mapper url="c:/mapper/UserMapper.xml" />
     <mapper class="com.starbooks.mapper.UserMapper" />
     <package name="com.starbooks.mapper" />
 </mappers>

映射文件 mapper

select 标签 
属性 说明
parameterType属性 可选,因为typeHandler会自动判断类型
flushCache 可选是否结果后清空缓存和二级缓存
useCache 是否开启二级缓存
statementType 使用jdbc哪种statement工作,PREPARED(默认) STATEMENT CALLABLE
insert标签多的属性
属性 说明
keyProperty属性 把插入或者更新返回值给po的某个属性 通常是设置主键
keyColumb属性 设置第几个是主键
useGeneratedKeys属性 此属性会让Mybatis 使用jdbc的getGeneratedKeys方法获取数据库内部产生的主键

以下代码,都可以实现mysql主键的自增主键,并返回

<insert id="addUser-mysql" parameterType="user" keyProperty="id" useGeneratedKeys="true">
      insert into user1 values (#{
    
    id}, #{
    
    name},  #{
    
    age}, #{
    
    points})
</insert>
<insert id="addUser1-mysql" parameterType="user">
     <selectKey keyProperty="id" resultType="Integer">
         select LAST_INSERT_ID()
     </selectKey>
     insert into user1 (name,pswd) values(#{
    
    name},#{
    
    pswd})
 </insert>
sql 标签定义可复用sql语句
<sql id="userCols">id, name, age, points</sql>
<select id="findUserFields" resultType="user">
   select <include refid="userCols"></include> from user1 where id = #{
    
    id}
</select>
resultMap标签 

子元素<id>用于表示哪个列是主键,
<result>用于表示POJO和数据表中普通列的映射关系。
<association>和<collection>用于处理多表时的关联关系,
<discriminator>元素主要用于处理一个单独的数据库查询返回很多不同数据类型结果集的情况。

<resultMap id="resMap" type="user">
    <id property="id" column="t_id"/>
    <result property="name" column="t_name" />
    <result property="age" column="t_age" />
</resultMap>

动态SQL

MyBatis 3采用了功能强大的基于OGNL的表达式来完成动态SQL

if

   <select id="findUserByNameAge" parameterType="user" resultType="user">
        select * from user1
        <if test="name != null and name != ''">
            where name like concat('%', #{
    
    name}, '%')
        </if>
    </select>

choose 使用 类似 switch case

    <select id="findUserBysome2" parameterType="user" resultType="user">
        select * from user1 where 1 = 1
        <choose>
            <when test="name != null and name != ''">
                and name like concat('%', #{
    
    name}, '%')
            </when>
            <when test="age != null">
                and age = #{
    
    age}
            </when>
            <otherwise>
                and points is not  null
            </otherwise>

        </choose>
    </select> 

where

 where 1 = 1 可以用where标签代替,判断组合条件下拼装的SQL语句,
 只有<where>元素内的条件成立时,才会在拼接SQL中加入where关键字,否则将不会添加;
 即使where之后的内容有多余的“AND”或“OR”,<where>元素也会自动将它们去除  

 trim 标签也可以实现 
 <trim prefix="where" prefixOverrides="and">
 ...同上 
 </trim>

set

   <update id="updateUser1" parameterType="user">
        update user1
        <set>
            <if test="name != null and name != ''">
                name = #{
    
    name}
            </if>
            <if test="age != null">
                 age = #{
    
    age}
            </if>
        </set>
        where id = #{
    
    id}
    </update>

foreach 拼接条件

<select id="findUserByids" parameterType="List" resultType="user">
    select * from user1 where id in 
    <foreach collection="list" item="id" index="index" open="(" close=")" separator=",">
        #{
    
    id}
    </foreach>
</select>

// 测试 
List<Integer> arr = new ArrayList<>();
arr.add(1);
arr.add(2);
arr.add(4);
List<User>  users = sqlSession.selectList("UserMapper1.findUserByids", arr);
for(User u : users){
    
    
  System.out.println(u.toString());
}

<bind>元素
在进行模糊查询编写SQL语句的时候,若使用“${}”进行字符串拼接,则无法防止SQL注入问题;若使用concat函数进行拼接,则只针对 MySQL数据库有效;若使用的是Oracle数据库,则要使用连接符号“||”

   <select id="findUserByName2" parameterType="String" resultType="user">
        <bind name="name111" value="'%' + name + '%'"
        select * from user1 where name like #{
    
    name111}
    </select>

MyBatis的关联映射

数据表之间以及对象之间的3种关联关系,一对一、一对多和多对多关联映射的使用、关联关系中的嵌套查询和嵌套结果。

数据库中的关联关系
一对一:在任意一方引入对方主键作为外键。
一对多:在“多”的一方添加“一”的一方的主键作为外键。
多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键

MyBatis在映射文件中加载关联关系对象主要通过两种方式:嵌套查询和嵌套结果
嵌套查询是指通过执行另一条SQL映射语句来返回预期的复杂类型;
优点是比较简单,但是嵌套查询的方式要执行多条SQL语句,对于大型数据集合和列表展示不好,因为可能会导致成百上千条关联的SOL语句被执行,从而极大地消耗数据库性能,并且会降低查询效率
嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集

<resultMap>元素中包含一个<association>子元素,MyBatis就是通过该元素来处理一对一关联关系的。
<association>元素中,通常可以配置以下属性。 
property:指定映射到的实体类对象属性,与表字段一一对应。 
column:指定表中对应的字段。
javaType:指定映射到实体对象属性的类型。
select:指定引入嵌套查询的子SQL语句,用于关联映射中的嵌套查询。 
fetchType:指定在关联查询时是否启用延迟加载,有lazy和eager两个属性值,默认值为lazy(默认关联映射延迟加载)。

一对一

// Student 类中的studentCard 属性指向 StudentCard 类 
<resultMap id="studentCardWithStudentResult" type="Student">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <result property="sex" column="sex" />
    <association
            property="studentCard"
            column="card_id"
            select="studentCardMapper.findCardById"
            javaType="StudentCard" />
</resultMap>

使用 嵌套查询 进行关联查询映射,使用延迟加载可以降低运行消耗并提高查询效率。默认没开启延迟加载,需在核心配置文件mybatis-config.xml中的<settings>元素内进行配置,如下

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

使用嵌套结果

    <select id="findStudentById2"
            resultMap="studentCardWithStudentResult2"
            parameterType="Integer">
    select s.*,scard.code from student s, student_card scard where s.card_id = scard.id and s.id=#{
    
    id}
    </select>

    <resultMap id="studentCardWithStudentResult2" type="Student">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="sex" column="sex" />
        <association property="studentCard"  javaType="StudentCard">
            <id property="id" column="card_id" />
            <result property="code" column="code" />
        </association>
    </resultMap>

一对多

链接这个讲的挺好


Redis数据库

  • Redis是一种运行在内存的Nosql数据库,支持7种数据类型的存储。Redis是一个开源C编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、键值数据库,并提供多种语言的API。还提供了简单的事务机制。
  • 查询比例较大的网站使用Redis可以数倍地提升网站的性能。例如,当一个会员登录网站,把常用数据从数据库一次性查询出来存放在Redis中,那么之后大部分的查询只需要基于Redis完成,这样将很大程度上提升网站的性能

spring boot 集成 redis 需要的包

 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <exclusions>
            <exclusion>
                <groupId>io.lettuce</groupId>
                <artifactId>lettuce-core</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
 <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
 </dependency>

Spring提供了一个RedisConnectionFactory接口,通过它可以生成一个RedisConnection接口对象,而RedisConnection接口对象是对Redis底层接口的封装

参考资料

  • Spring+Spring MVC+MyBatis从零开始学(吴为胜 杨章伟)
  • 深入浅出Spring Boot 2.x-杨开振
  • https://blog.csdn.net/qq_40147863/article/list/5
  • https://www.cnblogs.com/haizhilangzi/p/11055179.html

猜你喜欢

转载自blog.csdn.net/qq_29334605/article/details/107198307