毫无疑问,第一步引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.3.9.RELEASE</version>
</dependency>
第二步,编写application.yml的配置文件:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
connection-timeout: 30000
minimum-idle: 10
maximum-pool-size: 65
idle-timeout: 60000
max-lifetime: 600000
auto-commit: true
pool-name: MyHikariCP
leak-detection-threshold: 5000
connection-test-query: SELECT 1
jpa:
hibernate:
ddl-auto: update
show-sql: true
database: mysql
ORM框架的思想就是通过java对象去操作数据库,那么下面我们创建实体类,通过实体类去生成数据库表。
@EqualsAndHashCode(callSuper = true)
@Data
@Entity
@Table(name = "t_user")
@Accessors(chain = true)
public class User extends BaseEntity implements Serializable {
private String userName;
private String password;
private String age;
private String gender;
private String email;
}
对于实体类上共同有的一些字段的封装
@EqualsAndHashCode(callSuper = true)
@Data
@MappedSuperclass //不单独映射成一张表,但是将它和继承它的子类一起映射成一张表
@EntityListeners(AuditingEntityListener.class) //在jpa.save方法被调用的时候,时间字段会自动设置并插入数据库,但是CreatedBy和LastModifiedBy并没有赋值,因为需要实现AuditorAware接口来返回你需要插入的值。
public class BaseEntity extends LogicalDeleteEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@JsonIgnore
@CreatedBy
@Column(name = "created_by",nullable = false,updatable = false)
private String createdBy;
@JsonIgnore
@CreatedDate
@Column(name = "created_date",updatable = false)
private LocalDateTime createdDate;
@JsonIgnore
@LastModifiedBy
@Column(name = "last_modified_by")
private String lastModifiedBy;
@JsonIgnore
@LastModifiedDate
@Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate;
}
逻辑删除
@MappedSuperclass
public class LogicalDeleteEntity implements LogicalDeleteTable, Serializable {
private static final Long serialVersionUID = 1L;
@JsonIgnore
@Column(name = "is_deleted", nullable = false)
protected boolean deleted;
@Override
public boolean isDeleted() {
return this.deleted;
}
@Override
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
public interface LogicalDeleteTable {
boolean isDeleted();
void setDeleted(boolean deleted);
}
配置自动插入时间;此处应有登录功能,获取登录信息,设置创建人和修改人的
@Configuration
public class AuditorConfig implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
return Optional.of("张三");
}
}
配置启动类
@SpringBootApplication
@EnableJpaAuditing //自动插入时间
@EnableTransactionManagement //事务
@EnableJpaRepositories(basePackages = "com.ddh.springdatajpa.mapper") //相当于@MapperScan
public class SpringdatajpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringdatajpaApplication.class, args);
}
}
下面配置dao层,(个人习惯使用mybatis,所以命名风格上有所偏好,勿介勿介)
@Repository
public interface UserMapper extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
简单的API调用不说了,现在只想分享一下,JpaSpecificationExecutor的动态条件查询
一个封装的请求参数:
@Data
public class QueryParam<T> implements Serializable {
private T param;
private String searchValue;
}
service层进行业务的处理
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> searchByLike(QueryParam<User> param) {
User user = param.getParam();
String searchValue = param.getSearchValue();
List<User> userList = userMapper.findAll(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
ArrayList<Predicate> list = new ArrayList<>();
if (StringUtils.isNotBlank(user.getGender())) {
Predicate p1 = builder.equal(root.get("gender").as(String.class), user.getGender());
list.add(p1);
}
if (StringUtils.isNotBlank(searchValue)) {
Predicate p3 = builder.like(root.get("userName").as(String.class), "%"+searchValue+"%");
Predicate p4 = builder.like(root.get("email").as(String.class),"%"+searchValue+"%");
list.add(builder.or(p3,p4));
}
query.where(list.toArray(new Predicate[list.size()]));
return query.getRestriction();
}
});
return userList;
}
}
测试一下:
@SpringBootTest
@Slf4j
class SpringdatajpaApplicationTests {
@Autowired
UserService userService;
@Test
void contextLoads() {
User user = new User().setGender("女性").setUserName("杨");
log.info(user.toString());
QueryParam<User> param = new QueryParam<User>();
param.setParam(user);
param.setSearchValue("8");
List<User> users = userService.searchByLike(param);
log.info(users.toString());
}
}
控制台打印的sql:
select user0_.id as id1_0_, user0_.is_deleted as is_delet2_0_, user0_.created_by as created_3_0_, user0_.created_date as created_4_0_, user0_.last_modified_by as last_mod5_0_, user0_.last_modified_date as last_mod6_0_, user0_.age as age7_0_, user0_.email as email8_0_, user0_.gender as gender9_0_, user0_.password as passwor10_0_, user0_.user_name as user_na11_0_ from t_user user0_ where user0_.gender=? and (user0_.user_name like ? or user0_.email like ?)
控制台打印的结果:
[User(userName=秋香, password=948422, age=28, gender=女性, email=448494564@163.com), User(userName=杨幂, password=3481155, age=30, gender=女性, email=8425445@163.com)]
因为上面的业务踩过的坑:
1.做模糊查询的时候,要自己加 "%".
2.spring data jpa对逻辑删除不是很友好,直接调用delete()方法是物理删除。添加
@SQLDelete(sql = "update t_user set is_deleted=1 and id =? ")
@SQLDeleteAll(sql = "update t_user set is_deleted=1 and id =?")
@Where(clause = "id_deleted=0")
只能是对单个Entity有效,限制太大。
3.因为是多条件查询,如果你每一个条件都设一下query.where();那么前面的条件会被后面的覆盖掉
eg:
if (StringUtils.isNotBlank(user.getGender())) {
query.where(builder.equal(root.get("gender").as(String.class), user.getGender()));
}
if (StringUtils.isNotBlank(user.getUserName())){
query.where(builder.like(root.get("userName").as(String.class), "%"+searchValue+"%"));
}
//前面的条件没有了,被覆盖了