JPA 规范
JPA 1.0整合查询语言(Query)和对象关系映射(ORM)元数据定义
JPA 2.0在1.0的基础上,增加Criteria查询,元数据API以及校验支持
实体(Entities)
约束:
a 实体类必须使用@Entity标注或者XML描述
b 实体类至少包含一个默认构造器,并且构造器必须是public 或者 protected
c 实体必须是顶级类,不能是枚举或者接口
d 实体类禁止是final类
c 实体支持继承,多态关联 以及多态查询
实体持久字段和属性
实体持久状态由字段(Fields)或者(Properties),字段即实例的属性或变量,属性则是JavaBeans实例的setter或getter方法
实例字段的访问级别必须是private,protected 或者 包可见,属性(getter, setter)的可见性必须是public或者protected
字段和属性可能是单一类型值或集合类型值
字段和属性访问类型(Access Type)是通过字段访问 还是 通过属性 访问
默认访问类型 (不需要持久化)
非transient 或者 @Transient 字段
非 @Transient属性
显示访问类型
注解类型
a 实体类
b 映射超类
c 嵌套类
注解
a @Access(AccessType.FIELD)字段
b @Access(AccessType.PROPERTY)属性
实体主键(Primary Key)
每个实体必须存在主键,主机必须定义在实体类
a 简单主键 @Id
b 复合主键 @EmbeddedId @IdClass
实体关系
实体关系可能一对一,一对多,多对一 或 多对多,这些关系是多态性的,可以是单向或者双向
注解表述方式
@OneToOne
@OneToMany
@ManyToOne
@ManyToMany
XML表述方式
@Embeddedle
@IdClass
版本 springboot 1.5.9
基本使用
第一步 配置数据源 |
datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/dbgirl username: root password: root type: com.alibaba.druid.pool.DruidDataSource initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false maxPoolPreparedStatementPerConnectionSize: 20 # 打开PSCache ,并且指定每个连接上PSCache的大小 poolPreparedStatements: true # 配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙 filters: stat,wall,log4j # 通过connectionProperties属性来打开mergeSql 功能,慢SQL 记录 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 jpa: hibernate: ddl-auto: update show-sql: true |
application |
package cn.sd.girl;
import cn.sd.girl.girl.interceptor.DefaultInterceptor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ErrorPage; import org.springframework.boot.web.servlet.ErrorPageRegistrar; import org.springframework.boot.web.servlet.ErrorPageRegistry; import org.springframework.http.HttpStatus; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@SpringBootApplication public class GirlApplication extends WebMvcConfigurerAdapter implements ErrorPageRegistrar{
public static void main(String[] args) {
SpringApplication.run(GirlApplication.class, args); }
} |
编写 doMain |
package cn.sd.girl.jpa.doMain;
import javax.persistence.*; import java.io.Serializable;
@Entity @Table(name = "deparment") public class Deparment implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name;
public Deparment(){}
@Override public String toString() { return "Deparment{" + "id=" + id + ", name='" + name + '\'' + '}'; }
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } } |
package cn.sd.girl.jpa.doMain;
import javax.persistence.*; import java.io.Serializable;
@Entity @Table(name="role") public class Role implements Serializable{
public Role() { }
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() {
return name; }
public void setName(String name) { this.name = name; }
@Override public String toString() { return "Role{" + "id=" + id + ", name='" + name + '\'' + '}'; } } |
package cn.sd.girl.jpa.doMain;
import com.fasterxml.jackson.annotation.JsonBackReference; import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.*; import java.io.Serializable; import java.util.Date; import java.util.List;
@Entity @Table(name = "user") public class User implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createDate; @ManyToOne @JoinColumn(name = "did") @JsonBackReference //防止关系对象的递归访问 private Deparment deparment;
/* cascade 默认被级联, eager 非懒加载 */ @ManyToMany(cascade = {},fetch = FetchType.EAGER) @JoinTable(name = "user_role",joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "roles_id")}) // 外键 private List<Role> roles;
@Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", createDate=" + createDate + ", deparment=" + deparment + ", roles=" + roles + '}'; }
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Date getCreateDate() { return createDate; }
public void setCreateDate(Date createDate) { this.createDate = createDate; }
public Deparment getDeparment() { return deparment; }
public void setDeparment(Deparment deparment) { this.deparment = deparment; }
public List<Role> getRoles() { return roles; }
public void setRoles(List<Role> roles) { this.roles = roles; }
public User() { } } |
编写 repository |
package cn.sd.girl.jpa.repository;
import cn.sd.girl.jpa.doMain.Deparment; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;
@Repository public interface DepartmentRepository extends JpaRepository<Deparment,Long>{ } |
package cn.sd.girl.jpa.repository; import cn.sd.girl.jpa.doMain.Role; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;
@Repository public interface RoleRepository extends JpaRepository<Role,Long>{ } |
package cn.sd.girl.jpa.repository;
import cn.sd.girl.jpa.doMain.User; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; /* JPA 还提供了一些自定义方法的规章,例如,在接口中使用关键字findBy ,readBy,getBy作为方法名的前缀,拼接实体类中的属性字段(首个字母大写), 并可选择拼接一些SQL查询关键字组合成一个查询方法 */ @Repository public interface UserRepository extends JpaRepository<User,Long>{ // And User findByIdAndName(Long id, String name);
/* @Query 来定义一些简单的查询语句 */ @Query("select t from User t where t.name like :name") Page<User> findByName(@Param("name") String name, Pageable pageable);
}
|
编写 controller |
package cn.sd.girl.jpa.controller;
import cn.sd.girl.girl.ExceptionEnum; import cn.sd.girl.girl.domain.Girl; import cn.sd.girl.girl.exception.GirlException; import cn.sd.girl.girl.repository.GirlRepository; import cn.sd.girl.jpa.doMain.Deparment; import cn.sd.girl.jpa.doMain.Role; import cn.sd.girl.jpa.doMain.User; import cn.sd.girl.jpa.repository.DepartmentRepository; import cn.sd.girl.jpa.repository.RoleRepository; import cn.sd.girl.jpa.repository.UserRepository; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.jconsole.DruidDataSourcePanel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.util.Assert; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid; import java.util.Date; import java.util.List;
@RestController public class JpaController {
private static Logger logger = LoggerFactory.getLogger(JpaController.class); @Autowired UserRepository userRepository; @Autowired DepartmentRepository departmentRepository; @Autowired RoleRepository roleRepository;
@RequestMapping(value = "befor") public void befor(){ userRepository.deleteAll(); roleRepository.deleteAll(); departmentRepository.deleteAll();
Deparment deparment = new Deparment(); deparment.setName("开发部"); departmentRepository.save(deparment); Assert.notNull(deparment.getId()); Role role = new Role(); role.setName("admin"); roleRepository.save(role); Assert.notNull(role.getId());
User user = new User(); user.setName("user"); user.setCreateDate(new Date()); user.setDeparment(deparment);
List<Role> roles = roleRepository.findAll(); Assert.notNull(roles); user.setRoles(roles);
userRepository.save(user); Assert.notNull(user.getId()); }
@RequestMapping(value = "findPage") public void findPage(){
Pageable pageable = new PageRequest(0,10,new Sort(Sort.Direction.ASC,"id"));
Page<User> page = userRepository.findAll(pageable);
Assert.notNull(page);
User u = userRepository.findByIdAndName(new Long(1),"user");
Page<User> user1 = userRepository.findByName("user", pageable);
logger.info("u:"+u);
for(User user : user1.getContent()){ logger.info("==============user=======user name:{},department name:{},role name:{}", user.getName(),user.getDeparment(),user.getRoles()); } }
}
|
运行结果 |
2018-07-23 10:49:38.127 INFO 4368 --- [nio-8081-exec-1] cn.sd.girl.jpa.controller.JpaController : u:User{id=1, name='user', createDate=null, deparment=Deparment{id=1, name='开发部'}, roles=[Role{id=1, name='admin'}]} 2018-07-23 10:49:38.127 INFO 4368 --- [nio-8081-exec-1] cn.sd.girl.jpa.controller.JpaController : ==============user=======user name:user,department name:Deparment{id=1, name='开发部'},role name:[Role{id=1, name='admin'}] 2018-07-23 10:49:38.127 INFO 4368 --- [nio-8081-exec-1] cn.sd.girl.jpa.controller.JpaController : ==============user=======user name:user,department name:Deparment{id=1, name='开发部'},role name:[Role{id=1, name='admin'}, Role{id=3, name='普通用户'}, Role{id=4, name='admin'}] |
扩展JPA
第一部 编写 扩展类 接口 |
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.NoRepositoryBean;
import java.io.Serializable; @NoRepositoryBean public interface ExpandJpaRepository<T, ID extends Serializable> extends JpaRepository<T,ID> {
T findOne(String sql, Object... objects); } |
编写扩展类实现 |
import org.springframework.data.jpa.repository.query.QueryUtils; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import javax.persistence.EntityManager; import javax.persistence.Query; import java.io.Serializable; import java.util.Iterator;
public class ExpandJpaRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements ExpandJpaRepository<T,ID> {
private final EntityManager entityManager; private final JpaEntityInformation<T, ?> entityInformation;
public ExpandJpaRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); this.entityManager = entityManager; this.entityInformation = entityInformation; }
private void applyQueryParameter(Query query,Object... objects){ if(objects!=null){ int i = 0; for(Object value:objects){ i++; query.setParameter(i,value); } } }
@Override public T findOne(String sql, Object... objects) { Query query = entityManager.createQuery(sql);
applyQueryParameter(query,objects);
T result = (T)query.getSingleResult(); return result; }
} |
编写装配类 getTargetRepository 就是返回 ExpandJpaRepositoryImpl |
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import javax.persistence.EntityManager; import java.io.Serializable;
public class ExpandJpaRepositoryFactoryBean<R extends JpaRepository<T, ID>, T, ID extends Serializable> extends JpaRepositoryFactoryBean<R, T, ID> {
public ExpandJpaRepositoryFactoryBean(Class<? extends R> repositoryInterface) { super(repositoryInterface); }
protected RepositoryFactorySupport createRepositoryFactory( EntityManager entityManager) { return new ExpandJpaRepositoryFactory<T, ID>(entityManager); }
private static class ExpandJpaRepositoryFactory<T, ID extends Serializable> extends JpaRepositoryFactory {
private final EntityManager entityManager;
public ExpandJpaRepositoryFactory(EntityManager entityManager) {
super(entityManager); this.entityManager = entityManager; }
protected Object getTargetRepository(RepositoryMetadata metadata) { JpaEntityInformation<T, Serializable> entityInformation = (JpaEntityInformation<T, Serializable>) getEntityInformation(metadata.getDomainType()); return new ExpandJpaRepositoryImpl<T, ID>(entityInformation, entityManager); }
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { return ExpandJpaRepositoryImpl.class; } } } |
编写 配置类,加载自定义装配 类 |
import cn.sd.girl.jpa.repository.ExpandJpaRepositoryFactoryBean; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement;
@Order(Ordered.HIGHEST_PRECEDENCE) @Configuration @EnableTransactionManagement(proxyTargetClass = true) @EnableJpaRepositories(basePackages = "cn.sd.girl.jpa.repository",repositoryFactoryBeanClass = ExpandJpaRepositoryFactoryBean.class) @EntityScan(basePackages = "cn.sd.girl.jpa.doMain") public class JpaConfiguration { @Bean PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){ return new PersistenceExceptionTranslationPostProcessor(); } } |
UserRepository 继承的就是 前面定义的接口ExpandJpaRepositoy,使用JPA 扩展接口与使用原来的JPA接口一样,调用方法基本相同,可以使用 扩展接口 抽象一些通用的方法,能使用 更灵活 |
@Repository public interface UserRepository extends ExpandJpaRepository<User,Long>{ // And User findByIdAndName(Long id, String name);
/* @Query 来定义一些简单的查询语句 */ @Query("select t from User t where t.name like :name") Page<User> findByName(@Param("name") String name, Pageable pageable);
} |
使用 接口 |
package cn.sd.girl.jpa.controller;
import cn.sd.girl.girl.ExceptionEnum; import cn.sd.girl.girl.domain.Girl; import cn.sd.girl.girl.exception.GirlException; import cn.sd.girl.girl.repository.GirlRepository; import cn.sd.girl.jpa.doMain.Deparment; import cn.sd.girl.jpa.doMain.Role; import cn.sd.girl.jpa.doMain.User; import cn.sd.girl.jpa.repository.DepartmentRepository; import cn.sd.girl.jpa.repository.RoleRepository; import cn.sd.girl.jpa.repository.UserRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.util.Assert; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid; import java.util.Date; import java.util.List;
@RestController public class JpaController {
private static Logger logger = LoggerFactory.getLogger(JpaController.class); @Autowired UserRepository userRepository;
@RequestMapping(value = "findOne") public void findOne(){ String sql = "select t from User t where t.id = ?1"; User one = userRepository.findOne(sql, new Object[]{Long.valueOf(1)}); System.out.println("one:"+one); }
}
|