一、maven配置文件加入依赖
Spring Data JPA 依赖,最新稳定的版本为1.10.1.RELEASE,这里需要说明下的是,其依然依赖hibernate JPA相关JAR,hibernate-core之类的是不需要的。hibernate是JPA规范的一种实现,所以需要加入其依赖。ehcache是hibernate二级缓存的配置,不是必须的。
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.10.1.RELEASE</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>5.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.4.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>5.1.0.Final</version> </dependency>
二、声明持久层接口
这里给大家说说几个常见的很重要的核心类文件,源码下面也贴上了。
大家可以看到这个类的继承结构,这里值得说的是,Spring Data JPA的持久层的借口命名是有一套规范的,后面你加入的自定义方法满足其规范之后,你的实现类都是可以不需要写的,而且事务管理也帮你做好了。是不是觉得很神奇呢!
JpaRepository继承了PagingAndSortingRepository、QueryByExampleExecutor,PagingAndSortingRepository又继承了CrudRepository,CrudRepository 大家通过看名字也应该是干啥的,其就是CRUD的,PagingAndSortingRepository里面加了分页的相关方法。 CrudRepository.java package org.springframework.data.repository; import java.io.Serializable; @NoRepositoryBean public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> { <S extends T> S save(S entity); <S extends T> Iterable<S> save(Iterable<S> entities); T findOne(ID id); boolean exists(ID id); Iterable<T> findAll(); Iterable<T> findAll(Iterable<ID> ids); long count(); void delete(ID id); void delete(T entity); void delete(Iterable<? extends T> entities); void deleteAll(); } Repository.java 是一个空的接口 package org.springframework.data.repository; import java.io.Serializable; public interface Repository<T, ID extends Serializable> { } 大伙可以看看其源码,源码里面的注释我就不贴出来了,大家看方法名应该就知道其具体的意思了。 package org.springframework.data.jpa.repository; import java.io.Serializable; import java.util.List; import javax.persistence.EntityManager; import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.QueryByExampleExecutor; @NoRepositoryBean public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { List<T> findAll(); List<T> findAll(Sort sort); List<T> findAll(Iterable<ID> ids); <S extends T> List<S> save(Iterable<S> entities); void flush(); <S extends T> S saveAndFlush(S entity); void deleteInBatch(Iterable<T> entities); void deleteAllInBatch(); T getOne(ID id); @Override <S extends T> List<S> findAll(Example<S> example); @Override <S extends T> List<S> findAll(Example<S> example, Sort sort); }
大家用maven的时候,可以将源码顺便下下来,myeclipse里面设置一下即可,如图所示:
我这里的接口如下:
package com.example.dao; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import com.example.entity.UserInfo; public interface UserDaoSpringJpa extends JpaRepository<UserInfo,String>{ public List<UserInfo> findByUsername(String username);//注意这个命名,Spring会自动根据这个username去数据库里面取值,可以看到后面生成的sql语句,where条件里面就加了 where user_name =?,这就是Spring Data JPA查询命名的方便之处,只要按规则命名,他会采取一定的策略,通过解析方法名称创建查询,来生成sql语句。 }
JpaRepository<UserInfo,String> 这里是泛型接口,UserInfo是我们的实体类,String是主键类型,也称为id,大家以前写hibernatebasedao的时候可能是见过了的。这叫 泛型限定式依赖注入,也是Spring4的一大特性。之前有篇博文也提到了 http://enetq.blog.51cto.com/479739/1783339
三、在接口中声明自定义业务方法
Spring Data JPA将会根据指定的策略(后续会有文章说明,本此只是简单的介绍下,能跑起来。)为该方法生成实现代码,用户不需要实现该接口,这样就只需要写接口就行了。
四、Spring配置文件相关配置
配置文件中加入 jpa:repositories,启用扫描并自动创建代理 Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring autowired来直接使用该对象。
此需要jpa命名空间:
xmlns:jpa=" xsi:schemaLocation=" http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd "
具体的配置参加如下:各参数的含义配置文件里面已经给出了详细的解释,同时给出了相关配置类的源码。
一些配置的属性,大家在源码里面也是可以找到的。
<!-- 启用扫描并自动创建代理 Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录, 为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean, 业务层便可以通过 Spring autowired来直接使用该对象。 --> <jpa:repositories base-package="com.example.service" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"/> <!-- 通过entityManagerFactory的createEntityManager()方法获取EntityManager --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <!-- 指定数据源 --> <property name="dataSource" ref="dataSource"/> <property name="packagesToScan" value="com.example"/> <!-- 用于指定持久化实现厂商类 --> <property name="persistenceProvider"> <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/><!-- 替换 org.hibernate.ejb.HibernatePersistence --> </property> <!-- jpaVendorAdapter :用于设置实现厂商JPA实现的特定属性, 目前Spring提供了HibernateJpaVendorAdapter、OpenJpaVendorAdapter、EclipseLinkJpaVendorAdapter、 三个实现。 TopLinkJpaVendorAdapter在Spring4里面已经删除了, 目前支持的数据库如下: DB2, DERBY, H2, HSQL, INFORMIX, MYSQL, ORACLE, POSTGRESQL, SQL_SERVER, SYBASE, 这个类型在Spring提供的枚举类型 Database 里面有详细定义--> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="generateDdl" value="false"/> <property name="database" value="MYSQL"/> <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"/> <property name="showSql" value="true"/> </bean> </property> <!-- jpaDialect:用于指定一些高级特性,如事务管理,获取具有事务功能的连接对象等, 目前Spring提供HibernateJpaDialect、OpenJpaDialect 、EclipseLinkJpaDialect --> <property name="jpaDialect"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/> </property> <!-- 用于指定JPA属性,跟之前hibernateProperties是一致的 --> <property name="jpaPropertyMap"> <map> <entry key="hibernate.dialect" value="${hibernate.dialect}"/> <entry key="hibernate.show_sql" value="${hibernate.show_sql}"/> <entry key="hibernate.format_sql" value="false"/> <entry key="hibernate.query.substitutions" value="${hibernate.query.substitutions}"/> <entry key="hibernate.default_batch_fetch_size" value="${hibernate.default_batch_fetch_size}"/> <entry key="hibernate.max_fetch_depth" value="${hibernate.max_fetch_depth}"/> <entry key="hibernate.generate_statistics" value="${hibernate.generate_statistics}"/> <entry key="hibernate.bytecode.use_reflection_optimizer" value="${hibernate.bytecode.use_reflection_optimizer}"/> <!-- 缓存Cache配置 --> <entry key="hibernate.cache.provider_class" value="${hibernate.cache.provider_class}"/> <entry key="hibernate.cache.use_second_level_cache" value="${hibernate.cache.use_second_level_cache}"/> <entry key="hibernate.cache.use_query_cache" value="${hibernate.cache.use_query_cache}"/> <entry key="hibernate.cache.region.factory_class" value="${hibernate.cache.region.factory_class}"/> <entry key="net.sf.ehcache.configurationResourceName" value="${net.sf.ehcache.configurationResourceName}"/> <entry key="hibernate.cache.use_structured_entries" value="${hibernate.cache.use_structured_entries}"/> </map> </property> </bean> <!--指定事务管理器--> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean>
jpaVendorAdapter 相关参数的设置AbstractJpaVendorAdapter 类里面有详细说明:如下图所示:
五、测试
package com.example.action; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import com.example.entity.UserInfo; import com.example.service.impl.UserInfoService; import com.google.gson.Gson; @Controller public class UserInfoAction { @Autowired private UserInfoService userInfoService; private Logger log = Logger.getLogger(this.getClass()); @RequestMapping("findByUserName.do") public void findByUserName(HttpServletRequest request, HttpServletResponse response, ModelMap map) throws IOException{ List<UserInfo> listUserInfo = userInfoService.findByUsername("wj"); for(UserInfo userInfo:listUserInfo){ log.info(userInfo.getUsername()+":"+userInfo.getEmail()); } Gson gson = new Gson(); String jsonStr = gson.toJson(listUserInfo); response.getWriter().println(jsonStr); } @RequestMapping("saveUser.do") public void saveUser(HttpServletRequest request, HttpServletResponse response, ModelMap map) throws IOException{ List<UserInfo> listUserInfo = new ArrayList<UserInfo>(); for(int i=0;i<10;i++){ UserInfo userInfo = new UserInfo(); userInfo.setId(UUID.randomUUID().toString()); userInfo.setUsername(Math.random()+"wj"+i); userInfo.setPassword(Math.random()+"ss"+i); listUserInfo.add(userInfo); } listUserInfo = userInfoService.save(listUserInfo);//可以直接保存一个list集合,当然这里只是测试,实际的批量新增,批量更新的时候,是需要到一定的条数之后,flush一下的,比如20条,这根hibernate是类似的。 userInfoService.delete("21");//删除 UserInfo userInfo = userInfoService.findOne("2eab2884-e0e9-419c-8871-1d198b399813"); userInfo.setUsername("zhangsan"); userInfoService.saveAndFlush(userInfo);//更新 Gson gson = new Gson(); String jsonStr = gson.toJson(listUserInfo); response.getWriter().println(jsonStr); } }
经过测试,大家发现 新增、查询、删除、修改都是正常的
Hibernate: select userinfo0_.id as id1_4_, userinfo0_.birthday as birthday2_4_, userinfo0_.contact as contact3_4_, userinfo0_.create_time as create_t4_4_, userinfo0_.create_user as create_u5_4_, userinfo0_.delete_flag as delete_f6_4_, userinfo0_.email as email7_4_, userinfo0_.last_login_ip as last_log8_4_, userinfo0_.last_login_time as last_log9_4_, userinfo0_.last_logout_time as last_lo10_4_, userinfo0_.modify_time as modify_11_4_, userinfo0_.online_state as online_12_4_, userinfo0_.password as passwor13_4_, userinfo0_.register_time as registe14_4_, userinfo0_.sex as sex15_4_, userinfo0_.user_state as user_st16_4_, userinfo0_.username as usernam17_4_ from rbac.user_info userinfo0_ where userinfo0_.username=? Hibernate: insert into rbac.user_info (birthday, contact, create_time, create_user, delete_flag, email, last_login_ip, last_login_time, last_logout_time, modify_time, online_state, password, register_time, sex, user_state, username, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) insert 语句打印了10条, delete from rbac.user_info where id=? Hibernate: update rbac.user_info set birthday=?, contact=?, create_time=?, create_user=?, delete_flag=?, email=?, last_login_ip=?, last_login_time=?, last_logout_time=?, modify_time=?, online_state=?, password=?, register_time=?, sex=?, user_state=?, username=? where id=?