springboot jpa 详细用法

本文将介绍springboot中jpa各种详细使用方法,之前那篇写的太急了,水地太多了


依赖

gradle构建,使用的是mysql数据库和Jpa框架

plugins {
    id 'org.springframework.boot' version '2.2.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'mysql:mysql-connector-java'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

配置文件

server:
  port: 10000

spring:
  application:
    name: jpa-project

  datasource:
    url: jdbc:mysql://localhost:33306/jpa-project
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    show-sql: true #打印sql

启动注解

@EnableJpaAuditing
@EntityScan("com.ladyishenlong.jpaproject.model")
@EnableJpaRepositories("jpa")
@SpringBootApplication
public class JpaProjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaProjectApplication.class, args);
    }

}

在项目入口的类里面需要添加如上代码中的这些注解

  • @EnableJpaAuditing 是使用 @CreatedBy, @CreatedDate,@LastModifiedDate,@LastModifiedBy这四个注解所需要添加的
  • @EntityScan 是表映射实体类所在包的路径,就是@Entity注解添加的实体类所在的包的路径
  • @EnableJpaRepositories 就是继承了JpaRepository的接口所在的包的位置

用户名配置

@Configuration
public class UserAuditorAware implements AuditorAware {
    @Override
    public Optional getCurrentAuditor() {
        //从security框架中获取用户信息
        //SecurityContext ctx = SecurityContextHolder.getContext();
        return Optional.of("配置用户名");
    }
}
  • 使用 @CreatedBy 和 @LastModifiedBy 时注入的值在这里可以配置
  • 通常配合spring security框架获取用户登录信息

表说明

BaseTable

@Data
@MappedSuperclass
public class BaseTable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @CreatedDate
    private Date createTime;

    @CreatedBy
    private String createBy;

    @LastModifiedDate
    private Date lastModifyTime;

    @LastModifiedBy
    private String lastModifyBy;

}
  • @MappedSuperclass 表示该实体类并非是任何表的映射,只是用于书写公共字段,包括主键字段,表的映射实体类继承该类即可
  • 子类需要添加 @EntityListeners(AuditingEntityListener.class) 且使用save()或者saveAll()方法才能使@CreatedDate,@CreatedBy,@LastModifiedDate, @LastModifiedBy 发挥作用
  • @CreatedDate,@CreatedBy 在insert的时候会有值,但是update的时候会变成null,需要先查出来赋值才行

ProfessionTable

@Data
@Entity
@Table(name = "profession")
@EqualsAndHashCode(callSuper = true)
@EntityListeners(AuditingEntityListener.class)
public class ProfessionTable extends BaseTable {

    private String proName;
}
  • 这是学生专业信息表,继承BaseTable的字段基础上添加了proName
  • 在新版本的idea中使用@Table注解映射表名会有红色的下划线,可以用idea自带的数据库连接工具连接数据库就能去除,当然不去除只要表名写对也没有什么问题

StudentTable

@Data
@Entity
@Table(name = "student")
@EqualsAndHashCode(callSuper = true)
@EntityListeners(AuditingEntityListener.class)
public class StudentTable extends BaseTable {

    private String name;

    @Column(name = "pro_id")
    private int proId;


    @OneToOne
    @JoinColumn(name = "pro_id", referencedColumnName = "id",
            insertable = false, updatable = false)
    private ProfessionTable profession;

}
  • 这个就是学生信息表,继承了BaseTable表的字段,再见了name字段和proId字段
  • proId字段对应的是profession表的主键,在Jpa里面使用 @OneToOne和@JoinColumn来进行表关联
  • @OneToOne用在一对一的关系,本文不再讲述一对多,多对多的表关联,因为过于复杂建议直接用原生sql来操作
  • @JoinColumn用于注解两张表之间的关系,其中name是本表的关联字段,referencedColumnName对应的从表的关联字段insertable和updatable设置插入和更新是否联动,这里全部设置成false
  • @OneToOne和@JoinColumn都是写在主表的注释上,可以从主表查从表,而不能从从表查主表

Jpa

public interface StudentJpa extends JpaRepository {

}
  • 普通地继承JpaRepository类,这里只用StudentTable的JpaRepository

简易数据库操作

查询

@RestController
public class TestController {

    @Autowired
    private StudentJpa studentJpa;

    @GetMapping("/")
    public Object test() {
        return studentJpa.findAll();
    }
}
[
    {
        "id": 1,
        "createTime": null,
        "createBy": null,
        "lastModifyTime": null,
        "lastModifyBy": null,
        "name": "藤丸立香",
        "proId": 1,
        "profession": {
            "id": 1,
            "createTime": null,
            "createBy": null,
            "lastModifyTime": null,
            "lastModifyBy": null,
            "proName": "人理修复"
        }
    },
    {
        "id": 2,
        "createTime": "2020-01-27T03:45:29.000+0000",
        "createBy": "我是用户名",
        "lastModifyTime": "2020-01-27T03:45:29.000+0000",
        "lastModifyBy": "我是用户名",
        "name": "阿提拉",
        "proId": 1,
        "profession": {
            "id": 1,
            "createTime": null,
            "createBy": null,
            "lastModifyTime": null,
            "lastModifyBy": null,
            "proName": "人理修复"
        }
    }
]
  • 这里可以看见关联表的数据是以实体类的方法放回的,并不是sql语句返回的拉平的格式,而且Hibernate生成打印出来的sql也是两句,并不是连表查询是一句搞定

插入,更新,删除

    @Transactional
    @GetMapping("/")
    public Object test() {
        StudentTable table=new StudentTable();
        table.setName("搞事情");
        studentJpa.save(table);
        
        studentJpa.deleteAll();
        
        return studentJpa.findAll();
    }
  • Jpa里面save()和saveAll()方法同时用于插入和更新,没有赋值的字段默认为null,如果更新的时候没有把原来的字段的值先查出来赋值也会变为null

自定义插入,更新,删除

JpaRepository中也可以自己写语句来进行插入或者更新,不过还是需要加上@Modifying注解

   @Modifying
    @Query("update StudentTable set name=:name where id=:id")
    void updateName(@Param("name") String name, @Param("id") int id);

    @Modifying
    @Query(nativeQuery = true,value = "update student set name = :name where id=:id")
    void updateName2(@Param("name") String name, @Param("id") int id);
  • 第一种是hql也就是Jpa的写法,idea在书写的时候会有提示,第二种是原生sql的写法
  • 这三种数据库操作需要加上@Transactional注解在事务中进行,不建议在controller中直接进行数据库操作,这里只是示例
  • 大量的插入和更新不适合用jpa的方法进行操作,效率很低建议直接用jdbcTemplate来进行操作
  • @Param必须添加来指定传入的参数在语句中的位置

复杂数据库操作

  • 增改删在数据量大不的情况下使用起来都很简单,但是复杂的查询操作很麻烦,所以这里的复杂数据库操作主要是复杂的查询
  • 这里的复杂操作都是指使用jpa的hql语句来查询,原生sql查询没有这些问题

查询部分字段

Jpa的方法都是默认查询全部字段,在实际业务中肯定不现实

@Query("select i.name as name from StudentTable i where i.id=:id")
StudentTable getNameById(@Param("id") int id);

  • 首先想到的是用这种写法查询,但是执行失败,代码会报错

dto 查询

Jpa中想要查询部分字段的方法我找到的是使用dto

public interface StudentDto {
    String getName();
}
@Query("select i.name as name from StudentTable i where i.id=:id")
StudentDto getNameById(@Param("id") int id);

如上代码所示,查询出来的就只有name值,且Hibernate生成的sql为:

select studenttab0_.name as col_0_0_ from student studenttab0_ where studenttab0_.id=?
  • dto中字段命名必须与get该字段的table中的方法名一致,且hql语句中需要添加as否则可能出现赋值失败该字段返回null的可能

dto 连表查询

dto查询能够解决单表查询部分字段的问题,但是连表查询依旧有问题

public interface ProfessionDto {
    String getProName();
}

public interface StudentDto {
    String getName();
    ProfessionDto getProfession();
}
@Query("select i.name as name ,i.profession as profession " +
            "from StudentTable i where i.id=:id")
StudentDto getNameById(@Param("id") int id);

返回结果为:

{
    "name": "藤丸立香",
    "profession": {
        "proName": "人理修复"
    }
}

然而Hibernate生成sql为

select studenttab0_.name             as col_0_0_,
       studenttab0_.pro_id           as col_1_0_,
       profession1_.id               as id1_0_,
       profession1_.create_by        as create_b2_0_,
       profession1_.create_time      as create_t3_0_,
       profession1_.last_modify_by   as last_mod4_0_,
       profession1_.last_modify_time as last_mod5_0_,
       profession1_.pro_name         as pro_name6_0_
from student studenttab0_
         inner join profession profession1_ on studenttab0_.pro_id = profession1_.id
where studenttab0_.id = ?
  • 主表查询的字段是部分的但是从表还是查了所有字段
  • 暂时没有找到解决查询从表部分字段的办法,所以复杂的连表查询建议使用原生sql语句

复杂查询条件

  • 这种查询我就不加累述了,Specification可以做到这一点,有兴趣的自行查询;但是Specification使用之后却又无法查询部分字段了,就显得很鸡肋,当然也可能是我没到方法
  • 复杂条件的查询主要是用于例如查询时候有这个参数就生效,没有就不生效的情况使用,用or语句可以做到这一点,但是降低了效率

执行原生sql

jpa执行原生sql

 @Modifying
 @Query(nativeQuery = true, value = "update student set name = :name where id=:id")
void updateName2(@Param("name") String name, @Param("id") int id);

@Query(nativeQuery = true,
            value = "select * from student where id = :id ")
 List> getNamesById(@Param("id") int id);

  • 在Jpa中执行原生sql只要nativeQuery属性设为true即可,不过查询语句的接收类型最好设置成 List>,这样转化为sql然后再转为实体类即可
  • 不过在JpaRepository下执行原生sql依旧不能满足部分复杂查询条件的使使用,就需要用到jdbcTemplate

NamedParameterJdbcTemplate

查询

  • 推荐使用NamedParameterJdbcTemplate而不是直接用JdbcTemplate,根据参数名传入参数
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
  • @Transactional注解依旧有效
 @GetMapping("/test2")
    public Object test2() {
        String sql = "select * from student where id=:id";
        HashMap map = new HashMap();
        map.put("id", 1);
        return jdbcTemplate.query(sql, map,new BeanPropertyRowMapper<>(StudentTable.class));
    }
  • NamedParameterJdbcTemplate查询返回结果可直接转为实体类
  • 这种写法sql语句可以在代码中拼接做到复杂查询的效果

批量插入或者更新

String sql2 = "update student set name=:name";
        List paraList = new ArrayList() {{
            this.add("这是name");
        }};

        SqlParameterSource[] sqlParameterSources = new SqlParameterSource[paraList.size()];
        for (int i = 0; i < paraList.size(); i++) {
            HashMap paraMap = new HashMap<>();
            paraMap.put("name", paraList.get(i));
            sqlParameterSources[i] = new MapSqlParameterSource(paraMap);
        }
        jdbcTemplate.batchUpdate(sql2, sqlParameterSources);

结论

  • 单表简单数据库操作用jpa自带的方法即可
  • 复杂数据库操作还是直接写原生sql更加方便点

你可能感兴趣的:(springboot jpa 详细用法)