Spring Data总结笔记

Spring Data概述

SpringData是SpringSource下的一个子项目,旨在简化对于数据库或者数据存储的持久化操作,包括存储和访问,并且不拘泥于SQL,它也支持NoSQL数据库如MongoDB。

在这里插入图片描述
Spring data下包含多个子项目如

  • Spring Data JPA
  • Spring Data MongoDB
  • Spring Data Redis
  • Spring Data Solr(搜索)

我们通过传统的方式,即原生JDBC或者Spring封装的JDBCTemplate的方式来访问数据库时,有这么一些缺点:

  • Dao层需要过多的代码
  • DaoImpl中有许多重复的代码
  • 我们进行分页或扩展其他功能的时候需要重写dao层
  • 等等等等…

那么使用Spring Data来访问数据库,我们所需的代码量极少,它能够大大提高我们的开发效率。

接下来介绍Spring Data持久化的操作

Spring Data操作实例

首先进行开发环境搭建,基于Spring、Maven的环境搭建这里不再详述,这里我们需要在xml配置文件当中,配置我们的JPA信息。

首先配置数据源:

<!--配置数据源文件,使用DBCP2连接池-->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="${driverClassName}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${userName}"/>
        <property name="password" value="${password}"/>
    </bean>

配置实体类管理工厂如下:

<!--配置实体管理工厂-->
    <bean id="entityManageFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
        </property>
        <property name="packagesToScan" value="com.lucky"></property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
            </props>
        </property>
    </bean>

这里我们将上述数据源引入,配置JPA适配器,因为我们需要使用注解进行操作,那么我们设置扫描包的路径,这里根据自己项目路径即可。接下来配置JPA一系列属性,包括命名策略,自动生成表(我们根据实体类生成表时需要用到),显示sql语句,格式化sql语句等。

接下来创建实体类如下:

package com.lucky.entity;


import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

/**
 * @Description 用户主体信息实体类
 *
 * @Author zhenxing.dong
 * @Date 2019/8/1 15:24
 */
@Entity
@Table(name = "t_user_main")
public class User {
    /**
     * 用户id
     */
    private int id;
    /**
     * 用户名
     */
    private String userName;
    /**
     * 用户邮箱
     */
    private String email;
    /**
     * 用户昵称
     */
    private String nickName;
    /**
     * 用户角色
     */
    private int role;

    //定义主键自增
    @Id
    @GenericGenerator(name = "generator", strategy = "increment")
    @GeneratedValue(generator = "generator")

    @Column(name = "id")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(name = "user_name")
    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Column(name = "email")
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Column(name = "nick_name")
    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    @Column(name = "role")
    public int getRole() {
        return role;
    }

    public void setRole(int role) {
        this.role = role;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", email='" + email + '\'' +
                ", nickName='" + nickName + '\'' +
                ", role=" + role +
                '}';
    }
}

实体类中@Table name,@Column name可对应表名和列名,也可不设置,让其自动生成,在id上添加注解,使用自增策略。

项目运行时便可在数据库中生成该表。

自动生成的表中字段会按照其默认策略生成,如varchar字段会自动生成255长度的字段,我们在这里也可同过@Column字段对其进行设置,如在getUserName()方法上添加注释@Column(length = 20),便可生成长度为20的varchar字段。

接下来配置事务管理器

<!--配置事务管理器-->
    <bean id="transcationManage" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManageFactory"></property>
    </bean>

具体事务管理器配置可见我另一篇博客
Spring事务管理机制

添加支持注解的事务

    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

配置Spring Data

    <jpa:repositories base-package="com.lucky"></jpa:repositories>

到这里我们的开发环境就已经搭建完成了。

(以上步骤,在使用SpringBoot的情况下,几乎均可省略!!!仅需配置数据源例如:
在这里插入图片描述

那么我们开始编写我们的持久层接口:
例如:

package com.hx.kindergarten.mapper;

import com.hx.kindergarten.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/**
 *
 * @param
 * @return
 * @author zhenxing.dong
 */
public interface UserDao extends Repository<User,Integer> {
    /**
     *
     * @param userName
     * @return
     */
    List<User> findAllByUserName(String userName);

    /**
     * 根据用户名查找用户
     * @param userName
     * @return user
     */
    User findUserByUserName(String userName);

}

可以看到,我们在使用JPA的时候,所需要做的仅仅是继承它的一个类JpaRepository,其后两个参数,第一个是我们要操作的对象类型,这里我们操作User的实体类,那么第一个参数就为User,第二个参数是操作对象对应的主键类型,我们在User中使用Integer类型作为主键,那么这里便使用Integer。

接下来声明方法,例如我们通过用户名查询User对象,那么如上,我们仅需声明这个接口,甚至不需要写任何的实现类,框架底层会通过我们的方法名通过一些规则能帮我们生成好。

Spring Data 进阶

Repository接口详解

  • Repository接口是Spring Data的核心接口,它不提供任何方法
  • public interface JpaRepository<T, ID extend Serializable>{}
  • @RepositoryDefintion 的使用

Repository是一个空接口,也就是一个标记接口,当我们声明的接口实现该接口的时候,他会自动将我们的接口归入Spring容器中进行管理,当我们的声明类没有继承该接口时,我们其实还有另一种方法来声明该接口

使用注解的方式:

package com.hx.kindergarten.mapper;

import com.hx.kindergarten.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.RepositoryDefinition;

import java.util.List;

/**
 *
 * @param
 * @return
 * @author zhenxing.dong
 */
@RepositoryDefinition(domainClass = User.class,idClass = Integer.class)
public interface UserDao //extends Repository<User,Integer>
 {
    /**
     *
     * @param userName
     * @return
     */
    List<User> findAllByUserName(String userName);

    /**
     * 根据用户名查找用户
     * @param userName
     * @return user
     */
    User findUserByUserName(String userName);

}

如上,@RepositoryDefinition(domainClass = User.class,idClass = Integer.class)这一行注解,也可以定义我们的持久化类,@RepositoryDefinition中有两个参数:
在这里插入图片描述
如上,domainClass定义我们的实体类,idClass定义实体类的主键类型。

Repository接口的子接口

Repository接口下具有许多的子接口,包括:
在这里插入图片描述

我们常用的几个:

  • CrudRepository<T, ID> —— 封装了简单的增删改查操作
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.data.repository;

import java.util.Optional;

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {

    <S extends T> S save(S var1);

    <S extends T> Iterable<S> saveAll(Iterable<S> var1);

    Optional<T> findById(ID var1);

    boolean existsById(ID var1);

    Iterable<T> findAll();

    Iterable<T> findAllById(Iterable<ID> var1);

    long count();

    void deleteById(ID var1);

    void delete(T var1);

    void deleteAll(Iterable<? extends T> var1);

    void deleteAll();
}

  • PagingAndSortingRepository<T, ID>——继承自CrudRepository<T, ID>,实现了分页与排序功能,我们在业务中需要进行分页和排序的时候,可直接使用该接口。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.data.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort var1);

    Page<T> findAll(Pageable var1);
}
  • JpaRepository<T, ID> ——继承了PagingAndSortingRepository<T, ID>接口,同时它实现了JPA相关的一些规范。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.data.jpa.repository;

import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll();

    List<T> findAll(Sort var1);

    List<T> findAllById(Iterable<ID> var1);

    <S extends T> List<S> saveAll(Iterable<S> var1);
    
	//JPA或Hibernate中都对应有会话中的状态之类的标记,这里可刷新
    void flush();

    <S extends T> S saveAndFlush(S var1);

    void deleteInBatch(Iterable<T> var1);

    void deleteAllInBatch();

    T getOne(ID var1);

    <S extends T> List<S> findAll(Example<S> var1);

    <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

Spring Data中查询方法的规范与规则
在这里插入图片描述
在这里插入图片描述
我们上面说了,我们在使用Spring Data时,仅需继承Repository类或其子接口,在声明方法时按照一定规则来声明,那么Spring框架底层会自动帮助我们来生成实现类执行这些方法,那么它既然能够帮助我们生成我们的具体方法实现,必然是有一定的规则的,上面的表格就是,我们在声明方法时需要遵循的一些命名规则,框架底层会帮助我们将方法名翻译为对应的SQL语句来与数据库进行交互。我们在需要的时候查表即可。

当然,这样的方式实现会造成两个问题:

  • 方法名过于冗长
  • 对应复杂查询我们很难通过方法名来构造

那么如何解决这些问题呢?

答案就是通过@Query注解的使用,来避免写过于冗长或复杂方法。

@Query注解

  • 在Repository方法中使用,不需要遵循上述规则
  • 使用时只要将@Query定义在方法之上即可。
  • 支持命名参数以及索引参数的使用。
  • 支持本地查询。

例如我们要查询id最大的User对象:

@Query("select user from User u where u.userId = (select max(userId) from User)")
 User getMaxUserById();

通过占位符的方式,例如根据姓名和年龄查询:

  @Query("select user from User u where u.userName=?1 and u.age=?2 ")
     User getUserByUserNameAndAge2(String userName,Integer age);

通过参数的方式如下:

@Query("select user from User u where u.userName=:userName and u.age=:age ")
     User getUserByUserNameAndAge2(@Param(value = "userName") String userName, @Param(value = "age")Integer age);

更新及删除操作

在删除更新操作中,我们仅用@Query注解是不够的,需要多加一个@Modifying注解来允许我们的修改操作。

例如:

	 @Modifying
     @Query("UPDATE User u set u.userName =: userName where u.age =:age")
     void update(@Param(value = "userName") String userName, @Param(value = "age")Integer age);

当然,在执行更新或者删除时时需要设置事务的,一般在service方法中,添加@Transactional注解即可。

Spring Data 高级

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值