Springboot jpa 的用法

依赖

  • 使用mysql数据库和jpa框架
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'
    }
}

启动注解

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

//使用@createBy需要的注解
@EnableJpaAuditing 

//table实体类所在包
@EntityScan("com.ladyishenlong.jpaproject.model")

//jpa接口所在包
@EnableJpaRepositories("jpa")

@SpringBootApplication
public class JpaProjectApplication {

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

}

建表

  • 仅仅是示例使用,就只有两个简单的表,但由于有不少公共字段,所以建立了一个公共的实体类
import lombok.Data;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.util.Date;

@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;

}
  • basetable里面使用@MappedSuperclass表示是通用的字段,不对应任何表,里面也可以写id,映射表继承该实体类即可
  • 其中使用@CreatedDate, @CreatedBy, @LastModifiedDate , @LastModifiedBy来自动插入和更新创建信息和修改信息,但是子类需要加上 @EntityListeners(AuditingEntityListener.class)
    注解
  • 其中@CreatedDate, @LastModifiedDate 会自动获取当前时间,但是@ CreatedBy,以及 @LastModifiedBy需要有用户信息,所以还需要实现AuditorAware接口,
@Configuration
public class UserAuditorAware implements AuditorAware {
    @Override
    public Optional getCurrentAuditor() {
        //常用security框架来传入用户名
        //SecurityContext ctx = SecurityContextHolder.getContext();
        return Optional.of("我是用户名");
    }
}
  • 另外需要注意的是在更新的时候@CreatedDate, @CreatedBy会变成null,需要先查出来再复制进去,@CreatedDate, @CreatedBy, @LastModifiedDate , @LastModifiedBy这四个注解只会在使用jpa的save(),saveAll()等方法时候生效,使用原生sql需要自己传入这些数据

  • profession表如下

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

    private String proName;
}

  • student表如下
@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;

}
  • @Column可以用来映射表中字段,一般来说jpa框架是根据小驼峰命名规则自动映射的,命名规范的话不需要写该注解
  • @OneToOne,@JoinColumn是用来映射一对一关系的,name对应本表字段,referencedColumnName对应映射表的字段insertable = false, updatable = false则是阻断了插入和更新的联动关系;简单来说就是连表查询

JPA

public interface StudentJpa extends JpaRepository {


    List findAllById(int id);

    @Query("select i from StudentTable i where i.id=:id")
    List getById(@Param("id") int id);
    

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


}
  • jpa查询的写法如上所示,插入和更新的语句除了自带的save(),和saveall()需要在@Query加上 @Modifying,以及在调用这个语句的方法上添加事务注解 @Transactional
    -原生sql语句建议使用 List>来接收,再转化为实体类; 使用jpa的hql连表查询的时候,联动的表会变成一个实体类返回;另外jpa查询的时候默认返回所有实体类中的字段,即使在hql语句中指定了字段也无效;
[
    {
        "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": "人理修复"
        }
    }
]

dto

  • jpa如果想只返回部分字段,就需要使用dto投影,简单来说就是需要另外定义接口来接收返回的字段
public interface StudentDto {
    String getName();
}
  • 使用dto的时候指定字段的时候要用as来指定别名,否则就算字段名一致也可定导致返回结果为空
   @Query("select i.name as name from StudentTable  i")
    List getNames();

-查询结果示例

[
    {
        "name": "藤丸立香"
    },
    {
        "name": "阿提拉"
    }
]
  • 可即使是用dto,联表查询的时候,从表的字段还是全部返回的
  • jpa在遇到查询条件不确定的时候,例如有的需求就是某个字段不为空的时候生效,空的时候不生效,在语句里面使用需要加or,很耗费资源,jpa也提供了其他自定义查询条件的方法,但是无法自定义查询字段,会返回所有字段,所以很麻烦,建议直接使用jdbcTemplate直接用原生语句查询,特别是批量查询批量查询的语句,我推荐使用NamedParameterJdbcTemplate
@Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;

    
    @Transactional
    public Object hello() {

        List pa = new ArrayList() {{
            this.add(1);
        }};
        SqlParameterSource[] batchArgs = new SqlParameterSource[pa.size()];

        for (int i = 0; i < pa.size(); i++) {
            HashMap param = new HashMap<>();
            param.put("name", "名字");
            param.put("id", pa.get(i));
            batchArgs[i] = new MapSqlParameterSource(param);
        }
        String sql = "update student set name=:name where id=:id";
        jdbcTemplate.batchUpdate(sql, batchArgs);

        return null;
    }
  • 简单总结下,jpa的hql查询用于最简单的查询和保存比较方便特别是单表操作,但是涉及到多表的复杂sql语句检疫还是用原生sql语句方便

你可能感兴趣的:(Springboot jpa 的用法)