依赖
- 使用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<String> {
@Override
public Optional<String> 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<StudentTable, Integer> {
List<StudentTable> findAllById(int id);
@Query("select i from StudentTable i where i.id=:id")
List<StudentTable> getById(@Param("id") int id);
@Query(nativeQuery = true,
value = "select * from student where id = :id ")
List<Map<String, Object>> getNamesById(@Param("id") int id);
}
- jpa查询的写法如上所示,插入和更新的语句除了自带的save(),和saveall()需要在@Query加上 @Modifying,以及在调用这个语句的方法上添加事务注解 @Transactional
-原生sql语句建议使用 List<Map<String, Object>>来接收,再转化为实体类; 使用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<StudentDto> getNames();
-查询结果示例
[
{
"name": "藤丸立香"
},
{
"name": "阿提拉"
}
]
- 可即使是用dto,联表查询的时候,从表的字段还是全部返回的
- jpa在遇到查询条件不确定的时候,例如有的需求就是某个字段不为空的时候生效,空的时候不生效,在语句里面使用需要加or,很耗费资源,jpa也提供了其他自定义查询条件的方法,但是无法自定义查询字段,会返回所有字段,所以很麻烦,建议直接使用jdbcTemplate直接用原生语句查询,特别是批量查询批量查询的语句,我推荐使用NamedParameterJdbcTemplate
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Transactional
public Object hello() {
List<Integer> pa = new ArrayList<Integer>() {{
this.add(1);
}};
SqlParameterSource[] batchArgs = new SqlParameterSource[pa.size()];
for (int i = 0; i < pa.size(); i++) {
HashMap<String, Object> 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语句方便








网友评论