Elicpse中搭建SpringData+jpa
首选先附上整个案例的工程结构图
jpa_springData_test1
src
com.lhc.bean
Dept6.java
com.lhc.dao
DeptDao3.java
DeptDao3Impl.java
IDeptDao.java
IDeptDao2.java
MyRepository.java
com.lhc.service
DeptServiceImpl.java
IDeptService.java
com.lhc.test
SsspTest.java
applicationContext.xml
db.properties
WebContent
META-INF
MANIFEST.MF
WEB-INF
lib(lib中的jar包,在后面会讲到)
web.xml(web.xml中没有写什么配置内容,因为本案例主要是在text测试类中演示,如果做页面请求交互,可以配置springmvc+spring监听器)
1、 在eclipse中新建web工程
2、 项目右键,add library,选择ServerRuntime,添加服务器运行环境
3、 添加jar包
antlr-2.7.7.jar
c3p0-0.9.2.1.jar
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.3.jar
dom4j-1.6.1.jar
hibernate-c3p0-4.2.4.Final.jar
hibernate-commons-annotations-4.0.2.Final.jar
hibernate-core-4.2.4.Final.jar
hibernate-entitymanager-4.2.4.Final.jar
hibernate-jpa-2.0-api-1.0.1.Final.jar
javassist-3.15.0-GA.jar
jboss-logging-3.1.0.GA.jar
jboss-transaction-api_1.1_spec-1.0.1.Final.jar
log4j-1.2.14.jar
mchange-commons-java-0.2.3.4.jar
mysql-connector-java-5.1.7-bin.jar
ojdbc6.jar
slf4j-api-1.5.8.jar
slf4j-log4j12-1.5.8.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-data-commons-1.5.0.release.jar
spring-data-jpa-1.3.0.release.jar
spring-expression-4.0.0.RELEASE.jar
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
4、 创建实体类
package com.lhc.bean;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table
public class Dept6 {
private int id;
private String deptName;
private String loc;
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
5、 创建一个dao接口,接口需继承Repository,如:
//这里继承接口后,必须告诉它是对哪个实体类进行持久化操作,并且这个实体类的主键的类型是什么
public interface IDeptDao extends Repository<Dept6, Integer> {
//因为当前dao继承的是Repository,Repository是SpringData的核心接口,它并不提供任何方法,所以用户需要自己定义需要的方法,并且这些方法名称必须遵循一定的规则,比如保存用save,ID查询用findById,具体其他定义规则,请参考http://shensuqiao.iteye.com/blog/2096114中的说明。
//这个dao接口不需要有实现类,因为使用了springData,springData就是为了简化dao层,不需要写dao接口的实现类
//如果这个类继承的是CrudRepository(继承Repository,提供增删改查方法)或者PagingAndSortingRepository(继承CrudRepository,增加了分页查询和排序两个方法)或者JpaRepository(继承PagingAndSortingRepository,是针对JPA技术的接口,提供flush(),saveAndFlush(),deleteInBatch()等方法)接口,那么dao中没有特殊的需求,可以不用写方法定义,直接可以调用继承接口中已经定义好的方法
public void save(Dept6 dept);
}
6、 创建service接口和service实现类
public interface IDeptService {
public void save(Dept6 dept);
}
@Service //标明当前是一个service层
public class DeptServiceImpl implements IDeptService {
@Autowired //自定注入dao实例
private IDeptDao deptDao;
@Transactional //添加事务控制
public void save(Dept6 dept) {
//TODO Auto-generated method stub
deptDao.save(dept);
}
}
7、 src下创建一个db.properties文件(文件名可以更改,但是需要是一个properties文件),这里配置连接数据库的信息,本案例连接的是oracle,根据自己连接的数据库配置下面的属性值,内容如下:
jdbc.user=scott
jdbc.password=tiger
jdbc.driverClass=oracle.jdbc.OracleDriver
jdbc.jdbcUrl=jdbc:oracle:thin:@localhost:1521:orcl
8、 src下创建一个applicationContext.xml并进行配置,具体配置内容如下:
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpahttp://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!—自动扫描,为这个包下面带有@Service等这些注解的类创建实例,在遇到@Autowired时进行自动注入-->
<context:component-scanbase-package="com.lhc"></context:component-scan>
<!—加载properties资源文件 -->
<context:property-placeholderlocation="classpath:db.properties"/>
<!—配置数据源 -->
<beanid="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<propertyname="user" value="${jdbc.user}"></property>
<propertyname="password" value="${jdbc.password}"></property>
<propertyname="driverClass"value="${jdbc.driverClass}"></property>
<propertyname="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
</bean>
<!--配置 EntityManagerFactory -->
<beanid="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<propertyname="dataSource" ref="dataSource"></property>
<!--配置 JPA 提供商的适配器. 可以通过内部 bean 的方式来配置 -->
<propertyname="jpaVendorAdapter">
<beanclass="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
</property>
<!--配置实体类所在的包 -->
<propertyname="packagesToScan"value="com.lhc.bean"></property>
<!--配置 JPA 的基本属性. 例如 JPA 实现产品的属性 -->
<propertyname="jpaProperties">
<props>
<propkey="hibernate.show_sql">true</prop>
<propkey="hibernate.format_sql">true</prop>
<propkey="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!--配置 JPA 使用的事务管理器 -->
<beanid="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<propertyname="entityManagerFactory"ref="entityManagerFactory"></property>
</bean>
<!--配置支持基于注解是事务配置 -->
<tx:annotation-driventransaction-manager="transactionManager"/>
<!--让 Spring 为Dao声明的接口创建代理对象, Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,
为继承 Repository或其子接口的接口创建代理对象 -->
<!--transaction-manager-ref和query-lookup-strategy应该也可以不写 -->
<jpa:repositoriesbase-package="com.lhc.dao"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"query-lookup-strategy="create-if-not-found"/>
</beans>
9、 新建测试包,和测试内
public class SsspTest {
/**
* 添加
*/
public void test1() {
ApplicationContextctx =
newClassPathXmlApplicationContext("applicationContext.xml");
//下面两种方法,得到一个service的实例对象
// IDeptServlet deptService = (IDeptServlet)ctx.getBean("deptService");
IDeptServicedeptService = ctx.getBean(IDeptService.class);
Dept6dept1=new Dept6();
dept1.setDeptName("test1");
deptService.save(dept1);
}
}
到此为止,一个基本的springData+jpa的案例就做完了。
在自己的dao中,也可以支持原生sql写法和jpql写法,这些方法名称可以随意,如下:
//原生SQL
@Query(value="selectcount(id) as cou from dept6",nativeQuery=true)
public BigDecimal getDeptCount();
也支持jpql写法,如下:
//查询
@Query("fromDept6 d where d.id > ?1")
public Page<Dept6> findAll(Integer id);
//修改和删除必须要写@Modifying注解,而且必须要有事务,上面的Query也有事务,但是是只读事务,所以修改和删除必须自己开启事务
//jpql不支持insert
@Modifying
@Query("updateDept6 d set d.deptName='新部门' where d.id=:id")
public void updateDeptName(@Param("id") int id);
上面的原生sql和jpql,因为使用了@query注解,所以方法名称没有限制,但是如果不使用@query注解,随意写个方法名称,就会报错,因为方面名称,springdata会把根据方面名称的语意来转换成执行的jquery语句,下面备注下在dao中写方法名称的规则:
解析方法名
Spring DataJPA会通过解析用户在持久层接口中定义的方法名来生成相应的query语句。(详细的解析方法名的规则请参照Spring DataJPA官方文档)
例:
持久层接口中定义如下:
public interface UserRepository extends Repository<User, Long> { List<User> findByEmailAddressAndLastname(String emailAddress, String lastname); } |
将会解析为如下的query
select u from User u where u.emailAddress = ?1 and u.lastname = ?2 |
解析时能被识别的keyword和包含这些keyword的方法会被解析成什么样的Query,如下表所示。
Sample | JPQL snippet | |
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Between | findByStartDateBetween | … where x.startDate between 1? and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> age) | … where x.age not in ?1 |
使用@Query
可以在自定义的查询方法上使用@Query来指定该方法要执行的查询语句,比如:
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.emailAddress = ?1") User findByEmailAddress(String emailAddress); } |
注意:
1:方法的参数个数必须和@Query里面需要的参数个数一致
2:如果是like,后面的参数需要前面或者后面加“%”
使用@Param可以用命名参数来代替位置编号,将方法参数与 JPQL 中的命名参数对应。JPQL 语句中通过": 变量"的格式来指定参数
例:
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); } |
如果要生成更新类的Query语句,在@Query之前添加@Modifying即可。
例:
@Modifying @Query("update User u set u.firstname = ?1 where u.lastname = ?2") int setFixedFirstnameFor(String firstname, String lastname); |
注意:
1:方法的返回值应该是int,表示更新语句所影响的行数。
2:在调用的地方必须加事务,没有事务不能正常执行。
使用JPA NamedQueries
在JPA配置文件中定义
在META-INF文件下的JPA的配置文件orm.xml中,通过<named-query/>元素进行定义。
例:
<named-query name="User.findByLastname"> <query>select u from User u where u.lastname = ?1</query> </named-query> |
通过Annotation配置
在Entity Bean中使用@NamedQuery(或@NamedNativeQuery)进行配置。
例:
@Entity @NamedQuery(name = "User.findByEmailAddress", query = "select u from User u where u.emailAddress = ?1") public class User { } |
注意
① 上述两种方法都需要满足”DomainClass.methodName()”的命名规则。
② 无论是在JPA配置文件中使用<named-query/>定义还是在Entity Bean中使用@NamedQuery进行配置,
在持久层的接口中必须声明对应的方法。
例:
public interface UserRepository extends JpaRepository<User, Long> { List<User> findByLastname(String lastname); User findByEmailAddress(String emailAddress); } |
下面讲解下其他几个可以继承的接口
1、dao继承CrudRepository接口使用方法
CrudRepository 接口继承于 Repository 接口,并新增了简单的增、删、查等方法方法如下:
T save(T entity);//保存单个实体
Iterable<T> save(Iterable<?extends T> entities);//保存集合
T findOne(ID id);//根据id查找实体
boolean exists(ID id);//根据id判断实体是否存在
Iterable<T> findAll();//查询所有实体,不用或慎用!
long count();//查询实体数量
void delete(ID id);//根据Id删除实体
void delete(T entity);//删除一个实体
void delete(Iterable<?extends T> entities);//删除一个实体的集合
void deleteAll();//删除所有实体,不用或慎用!
CRUDRepository 的使用很简单,原来我们继承 Repository ,现在我们继承 CRUDRepository ,就可以使用 CRUDRepository 固定的一些方法,我们不用写 SQL 语句。
2、dao继承PagingAndSortingRepository接口
PagingAndSortingRepository继承了CrudRepository接口,除了拥有父接口的增删改查方法外,还增加了分页查询和排序两个方法,添加的方法如下:
Iterable<T>findAll(Sort sort);//排序
Page<T>findAll(Pageable pageable);//分页查询(含排序功能)
分页案例代码如下:
Servcie层关键代码:
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
@Autowired
private IDeptDao2deptDao2;
public Page<Dept6> queryPage() {
// TODOAuto-generated method stub
Sort sort =new Sort(Direction.DESC, "id");
Page<Dept6>page = deptDao2.findAll(new PageRequest(0, 10, sort));
return page;
}
Dao层关键代码:
publicinterface IDeptDao2 extends PagingAndSortingRepository<Dept6,Integer> {
//因为PagingAndSortingRepository已经有了分页和排序方法,所以这里不需要定义任何方法
}
3、dao继承JpaRepository接口
JpaRepository继承了PagingAndSortingRepository接口,这个接口提供了JPA的相关功能,提供了flush(),saveAndFlush(),deleteInBatch()等方法,方法如下:
List<T>findAll();//查找所有实体
List<T>findAll(Sort sort);//排序 查找所有实体
List<T>save(Iterable<? extends T> entities);//保存集合
void flush();//执行缓存与数据库同步
TsaveAndFlush(T entity);//强制执行持久化
voiddeleteInBatch(Iterable<T> entities);//删除一个实体集合
3、自定义Repository接口
1)创建一个自定义的Repository接口,代码如下:
publicinterface MyRepository {
public void test();
}
2)创建一个dao接口,代码如下:
publicinterface DeptDao3 extends JpaRepository<Dept6, Integer>,MyRepository {
}
3) 创建一个自定义Repository实现类,示例代码如下:
//注意:这个类的类名,必须是DeptDao3这个类名+Impl( 默认情况下, Spring Data 会在base-package 中查找 "接口名Impl" 作为实现类. 也可以通过 repository-impl-postfix 声明后缀)
public class DeptDao3Impl implements MyRepository {
@PersistenceContext
private EntityManager entityManager;
public void test() {
//TODO Auto-generated method stub
Dept6dept=entityManager.find(Dept6.class, 1);
System.out.println(dept.getDeptName());
}
}
4)最后就可以在自己的service层调用此dao方法了,关键代码如下:
@Autowired
private DeptDao3 deptDao3;
/**
* 调用自定义的Repository
*/
public voidtestMyRepository() {
// TODO Auto-generated method stub
deptDao3.test();
}