首页 > 代码库 > Spring Data JPA
Spring Data JPA
- 配置相关bean
- 数据源<context:property-placeholder ignore-resource-not-found="true" location="classpath:jdbc-${spring.profiles.active}.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><!-- 基本属性 url、user、password --><property name="url" value="${ds.url}"/><property name="username" value="${ds.username}"/><property name="password" value="${ds.password}"/><!-- 配置初始化大小、最小、最大 --><property name="initialSize" value="5"/><property name="minIdle" value="5"/><property name="maxActive" value="50"/><!-- 配置获取连接等待超时的时间 --><property name="maxWait" value="60000"/><!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --><property name="timeBetweenEvictionRunsMillis" value="60000"/><!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --><property name="minEvictableIdleTimeMillis" value="300000"/><property name="validationQuery" value="SELECT ‘x‘"/><property name="testWhileIdle" value="true"/><property name="testOnBorrow" value="false"/><property name="testOnReturn" value="false"/><!-- 打开removeAbandoned功能 --><property name="removeAbandoned" value="true"/><property name="removeAbandonedTimeout" value="1800"/><property name="logAbandoned" value="true"/><!-- 打开PSCache,并且指定每个连接上PSCache的大小,mysql 不使用 --><property name="poolPreparedStatements" value="false"/><!-- 配置监控统计拦截的filters --><property name="filters" value="stat"/><!-- 慢查询sql打印 --><property name="connectionProperties" value="druid.stat.slowSqlMillis=200;druid.stat.logSlowSql=true"/></bean>
- 实体扫描并生成Repository<bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"><property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/></bean><bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><property name="dataSource" ref="dataSource"/><property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/><property name="packagesToScan" value="com.qingbo.sapling"/><property name="jpaProperties"><props><!-- 命名规则 My_NAME->MyName 自动更新数据库表结构(仅适用于开发阶段,正式运行后最好是手动维护数据库表结构) --><prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop><prop key="hibernate.hbm2ddl.auto">update</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.format_sql">true</prop></props></property></bean>
<!-- 自动寻找Repository接口并提供实现类以支持@Autowired注入,包名支持通配符,repository-impl-postfix="impl" --><jpa:repositories base-package="com.qingbo.sapling"/> - 事务,校验,测试时需要加事务注解@Transactional,网站运行需要配置OpenEntityManagerInViewFilter <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><property name="entityManagerFactory" ref="entityManagerFactory"/></bean>
<!-- proxy-target-class为true时@Transactional需要注解实现类的方法,而false则使用java的基于接口的代理 -->
<tx:annotation-driven proxy-target-class="true"/>
<!-- 自动搜索providerClass,也可指定org.hibernate.validator.HibernateValidator ,需提供资源文件classpath:ValidationMessages.properties --><bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/> - maven插件任务配置pom.xml<!--querydsl--><plugin><groupId>com.mysema.maven</groupId><artifactId>apt-maven-plugin</artifactId><version>1.1.0</version><configuration><processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor></configuration><dependencies><dependency><groupId>com.mysema.querydsl</groupId><artifactId>querydsl-apt</artifactId><version>3.3.1</version></dependency></dependencies><executions><execution><phase>generate-sources</phase><goals><goal>process</goal></goals><configuration><outputDirectory>target/generated-sources/annotations</outputDirectory></configuration></execution></executions></plugin>
- 网站配置web.xml<filter><filter-name>sessionFilter</filter-name><filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class><init-param><param-name>singleSession</param-name><param-value>false</param-value></init-param></filter><filter-mapping><filter-name>sessionFilter</filter-name><url-pattern>*.html</url-pattern></filter-mapping>
- dao持久层接口Repository,数据库表实体类也在这一层
- 核心查询接口:可以实现以下接口,也可以注解@RepositoryDefinition编写自己的接口
CrudRepository,提供基于键值ID和实体Entity的增Create删Delete改Update查Retrieve,T findOne(ID id);
PagingAndSortingRepository,提供分页和排序功能,Page<T> findAll(Pageable pageable)
JpaRepository,增加批量操作和刷新输出功能,void flush();
JpaSpecificationExecutor,复杂查询接口(持久层查询分页),Page<T> findAll(Specification<T> spec, Pageable pageable); - 简单查询方法命名规范,以下方法只需定义接口无需自己写实现代码,
支持方法前缀:findBy+find+readBy+read+getBy+get,
支持属性名及And和Or拼接,
支持属性操作Between+LessThan+GreaterThan+IsNull+[Is]NotNull+Like+NotLike+OrderBy+Desc+Not+In+NotIn(支持集合数组或可变参数),
支持属性嵌套findByAddressZipCode(ZipCode)可查询address.zipCode(支持findByAddress_ZipCode以避免歧义addressCode.zip),
其他判断词还有:findByStartDateAfter,findByEndDateBefore,findByNameStartingWith,findByAgeOrderByName,findByActiveTrue,
支持排序和分页:Page<User> findByName(String name, Pageable pageable);//也支持Sort sort参数
List<User> findByName(String, Sort sort);//支持返回List结果,避免构建Page而进行其他查询(count计算总数等),同时需要再定义count接口
支持扩展自定义接口:extends MyInterfaceCustom, Repository,然后实现类MyInterfaceCustomImpl,注入@PersistenceContext EntityManager em; 自定义接口方法查库
支持自定义全局接口:MyRepository extends JpaRepository,MyRepositoryImpl extends SimpleJpaRepository implements MyRepository,然后定义MyRepositoryFactoryBean extends JpaRepositoryFactoryBean提供MyRepositoryFactory extends JpaRepositoryFactory,在getTargetRepository方法里返回new MyRepositoryImpl,最后在repositories配置factory-class即可 - 命名查询
- 配置文件META-INF/orm.xml<named-query name="User.findByLastname"><query>select u from User u where u.lastname = ?1</query></named-query>
- 注解实体类查询@Entity @NamedQuery(name = "User.findByEmailAddress", query = "select u from User u where u.emailAddress = ?1")public class User {}public interface UserRepository extends JpaRepository<User, Long> {List<User> findByLastname(String lastname);//spring会优先查找NamedQuery,其次分析方法名}
- 注解接口方法,支持简单统计sql查询语句(复杂条件统计还得研究JPA查询条件)public interface UserRepository extends JpaRepository<User, Long> {@Query("select u from User u where u.emailAddress = ?1")User findByEmailAddress(String emailAddress);}@Query(value=http://www.mamicode.com/"select count(ts_user_id) from ts_user group by register_type limit ?1, ?2", nativeQuery=true)List registerPage(int offset, int length);
- 使用命名参数public interface UserRepository extends JpaRepository<User, Long> {@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")User findByLastnameOrFirstname(@Param("lastname") String lastname, @Param("firstname") String firstname);}
- 定义更新方法,注意这里参数序号也许比上面命名参数更方便public class Snippet {@Modifying @Transactional @Query("update User u set u.firstname = ?1 where u.lastname = ?2")int setFixedFirstnameFor(String firstname, String lastname);}
- 复杂查询条件
Specification,复杂查询条件封装,Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
Predicate,条件组合
Root,查询根可以有多个
CriteriaQuery,提供得到查询根的方法
CriteriaBuilder,构件查询对象,CriteriaQuery<Object> createQuery(); - 简单查询代码Specification<TsUser> spec = new Specification<TsUser>() {@Overridepublic Predicate toPredicate(Root<TsUser> root, CriteriaQuery<?> query, CriteriaBuilder cb) {List<Predicate> predicates = new ArrayList<Predicate>();if(StringUtils.isNotBlank(userSearch.getUserName())) predicates.add(cb.like(root.<String>get("userName"), "%"+userSearch.getUserName()+"%"));query.where(predicates.toArray(new Predicate[0]));return null;}};
- 封装查询代码Specification<TsUser> spec = new Specification<TsUser>() {@Overridepublic Predicate toPredicate(Root<TsUser> root, CriteriaQuery<?> query, CriteriaBuilder cb) {Criteria<TsUser> c = new Criteria<TsUser>();c.add(Restrictions.like("userName", userSearch.getUserName()));// UserSearch是前端封装的查询表单对象c.add(Restrictions.between("createdAt", DateUtil.parse(costSearch.getDateFrom()), DateUtil.parse(costSearch.getDateTo())));//区间查询c.add(Restrictions.like("tsUserreg.userAlias", userSearch.getUserAlias()));//查询其他相关表字段c.add(Restrictions.eq("status", userSearch.getStatus()));// 精确查询return c.toPredicate(root, query, cb);}};
Restrictions.java片段public static SimpleExpression eq(String fieldName, Object value) {if(StringUtils.isEmpty(value))return null;return new SimpleExpression (fieldName, value, Operator.EQ);}public static BetweenExpression between(String fieldName, Object value, Object value2) {if(StringUtils.isEmpty(value) && StringUtils.isEmpty(value2)) return null;return new BetweenExpression(fieldName, value, value2);}
AbstractExpression,计算查询路径(支持属性嵌套,例如address.zipCode)public abstract class AbstractExpression implements Criterion {Path getExpression(Root<?> root, String fieldName) {Path expression = null;if(fieldName.contains(".")){String[] names = StringUtils.split(fieldName, ".");expression = root.get(names[0]);for (int i = 1; i < names.length; i++) {expression = expression.get(names[i]);}}else{ expression = root.get(fieldName); }return expression;}}
SimpleExpression,支持Equal、Like、LessThan等条件public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query, CriteriaBuilder builder) {Path expression = getExpression(root, fieldName);switch (operator) {case EQ:return builder.equal(expression, value);case LIKE:return builder.like((Expression<String>) expression, "%" + value + "%");}}BetweenExpression,支持区间查询public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query, CriteriaBuilder builder) {Path expression = getExpression(root, fieldName);if(StringUtils.isEmpty(value)) {return builder.greaterThanOrEqualTo(expression, (Comparable)value);}else if(StringUtils.isEmpty(value2)) {return builder.lessThanOrEqualTo(expression, (Comparable)value2);}else {return builder.between(expression, (Comparable)value, (Comparable)value2);}}
Criteria,public class Criteria<T> implements Specification<T>{private List<Criterion> criterions = new ArrayList<Criterion>();public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {if (!criterions.isEmpty()) {List<Predicate> predicates = new ArrayList<Predicate>();for(Criterion c : criterions){predicates.add(c.toPredicate(root, query,builder));}// 将所有条件用 and 联合起来if (predicates.size() > 0) {return builder.and(predicates.toArray(new Predicate[predicates.size()]));}}return builder.conjunction(); // 没有条件时相当于true}public void add(Criterion criterion){if(criterion!=null){criterions.add(criterion);}}} - domain领域层,领域对象是业务核心层,与持久层松散耦合(需要领域对象与表实体类之间转换);
- 查询分页,domain领域层接口只是衔接service业务层和dao持久层,将数据库表对象TsUser转换为领域对象Userpublic Page<User> userPage(Specification<TsUser> spec, Pageable pageable) {Page<TsUser> page = userRepository.findAll(spec, pageable);List<User> content = new ArrayList<User>();Iterator<TsUser> iterator = page.iterator();while(iterator.hasNext()) {content.add(User.formUser(iterator.next()));}return new PageImpl<User>(content, pageable, page.getTotalElements());}
- Audit审计,记录实体创建和修改的管理账户,
- domain领域对象需实现接口Auditable<U, ID>(U是审计类型,通常是用户名或用户实体类),void setCreatedBy(final U createdBy);
- orm.xml注册监听器<persistence-unit-metadata><persistence-unit-defaults><entity-listeners><entity-listener class="org.springframework.data.jpa.domain.support.AuditingEntityListener" /></entity-listeners></persistence-unit-defaults></persistence-unit-metadata>
- 激活AuditorAware的自定义实现,其方法T getCurrentAuditor();返回的就是审计的用户(可以是String用户名或User用户对象,是setCreatedBy(U)的参数)
<jpa:auditing auditor-aware-ref="yourAuditorAwareBean" /> - service业务层,面向前端页面封装表单对象,定义接口并调用domain层来实现(需要表单对象与领域对象之间转换),建议每个web项目都有各自的service层(如front-service,admin-service等)
- 查询分页,需要构建Specification查询条件,将领域对象User转换为页面表单对象UserItemSpecification<TsUser> spec = new Specification<TsUser>() {@Overridepublic Predicate toPredicate(Root<TsUser> root, CriteriaQuery<?> query, CriteriaBuilder cb) {Criteria<TsUser> c = new Criteria<TsUser>();c.add(Restrictions.like("userName", userSearch.getUserName()));c.add(Restrictions.eq("tsUserreg.status", userSearch.getStatus()));return c.toPredicate(root, query, cb);}};
Page<User> users = userService.userPage(spec, pageable);List<UserItem> userItems = new ArrayList<UserItem>();for (User user : users.getContent()) {UserItem userItem = UserItem.formUserItem(user);userItems.add(userItem);} - web控制层(按项目划分如front-web,admin-web等)
- 前端开发人员负责内容
页面:jsp或vm
控制:Controller
业务接口及页面对象:front-service,UserItem等,自定义接口并提供假数据展示到页面 - 后台开发人员负责内容
负责domain和dao层设计开发(如果前端开发人员面向domain层开发,则形成紧耦合,会影响项目进度)
负责实现front-service等服务项目的接口实现,提供数据库数据 - 数据校验和异常处理
- web层数据校验:spring会自动校验@Valid参数并将结果存入BindingResult对象,UserItem等对象需要使用@NotNull等校验注解
UserController.userUpdate(@Valid UserItem, BindingResult bindingResult, Model model){ if(bindingResult.hasErrors()) return NOT_VALID_PAGE; } - service+domain数据校验
建议用AOP实现,全局抛校验异常,然后转异常处理逻辑 - 异常处理,@Service("excetionResolver")public class ExceptionResolver extends DefaultHandlerExceptionResolver {@Overrideprotected ModelAndView doResolveException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) {if(ex instanceof AuthorizationException) {//未授权或过期则拒绝访问,校验失败异常可类似处理if(request.getRequestURI().contains("admin/home.html")) {return new ModelAndView("redirect:login.html");}return new ModelAndView("admin/denied");}return new ModelAndView("admin/error");//其他业务异常,显示错误页面}}
来自为知笔记(Wiz)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。