SpringDataJpa学习(2)——SpringDataJpa的单表使用
写在前面
本文上接SpringDataJpa学习(1)——Jpa学习,在学习了Jpa规范后,来学习一下SpringDataJpa的使用。SpringDataJpa是spring公司推出的对jpa规范的深层封装。SpringDataJpa极大简化了数据库访问层代码,可以让我们免除各种简单的sql语句。
使用SpringDataJpa的环境准备
导入坐标
<properties>
<spring.version>5.2.3.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<c3p0.version>0.9.1.2</c3p0.version>
<mysql.version>5.1.6</mysql.version>
</properties>
<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring begin -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring对orm框架的支持包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring end -->
<!-- hibernate begin -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
<!-- hibernate end -->
<!-- c3p0 begin -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- c3p0 end -->
<!-- log begin -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- el begin 使用spring data jpa 必须引入 -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.1-b06</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
<!-- el end -->
</dependencies>
书写配置文件
由于是spring的一套规范,我们肯定要写spring的配置文件,如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!-- spring 和 spring data jpa的配置 -->
<!-- 1.创建entityManagerFactory对象交给spring容器管理-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 配置的扫描的包(实体类所在的包) -->
<property name="packagesToScan" value="com.liuge.domain" />
<!-- jpa的实现方式-->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!-- jpa的供应商适配器 -->
<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.MySQLDialect" />
<!-- 是否显示sql -->
<property name="showSql" value="true" />
</bean>
</property>
<!-- jpa的方言: 高级的特性-->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
<!-- 2. 创建一个数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"/>
<property name="password" value="abc456"/>
<property name="jdbcUrl" value="jdbc:mysql:///jpatest?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</bean>
<!-- 3.整合SpringDataJpa -->
<jpa:repositories base-package="com.liuge.dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
<!-- 4.配置事务管理器-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 5.声明式事务-->
<!-- 6.配置包扫描注解-->
<context:component-scan base-package="com.liuge"/>
</beans>
配置映射关系
还是和之前一样,在实体类上配置:
@Entity
@Table(name = "cst_customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;
@Column(name = "cust_address")
private String custAddress;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_phone")
private String custPhone;
@Column(name = "cust_source")
private String custSource;
}
并且生成对应的get和set方法以及toString即可。
编写符合SpringDataJpa规范的Dao层接口
/**
* JpaRepository<操作的实体类类型,实体类中主键属性的类型>
* 封装了基本CRUD操作
* JpaSpecificationExecutor<操作的实体类类型>
* 封装了复杂查询(分页)
*
* @author wushen
*/
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {}
实际上,我们只需要定义一个接口然后继承规定好的接口并写好泛型即可。
使用SpringDataJpa
在配置好后,我们可以写一个测试类来测试一下:
测试根据id查询
@RunWith(SpringJUnit4ClassRunner.class) // 声明单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml") //指定spring容器的配置信息
public class CustomerDaoTest {
@Autowired
private CustomerDao customerDao;
/**
* 根据id查询
*/
@Test
public void testFindOne() {
Optional<Customer> one = customerDao.findById(3L);
if (one.isPresent()) {
System.out.println(one.get());
}
}
}
由于是Spring的相关组件,我们要在测试类上配置好spring的相关测试信息,并且设置自动注入。
这里我们测试了一个简单的findById方法,会返回一个封装类,这都是SpringDataJpa规定好的,我们通过isPresent()方法判空,然后get()方法取得该对象即可。
测试添加和修改
/**
* save:保存或者更新
* 根据传递的对象是否存在主键id,如果没有id主键属性:保存
* 存在id主键属性,根据id查询数据库,更新数据
*/
@Test
public void testSave() {
Customer customer = new Customer();
customer.setCustName("我的天呐");
customer.setCustLevel("VIP");
customer.setCustIndustry("吔屎国");
customerDao.save(customer);
}
/**
* 更新
*/
@Test
public void testUpdate() {
Customer customer = new Customer();
customer.setCustId(4L);
customer.setCustName("我的天呐");
customer.setCustLevel("VIP");
customer.setCustIndustry("吔屎国");
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String dateTime = simpleDateFormat.format(date);
customer.setCustName(dateTime);
customerDao.save(customer);
}
在接口定义中,save既是保存也是更新,这主要取决于是否包含id。
延迟加载的根据id查询
/**
* 根据id从数据库查询
*
* @Transactional 保证getOne正常运行
* <p>
* findById:em.find()
* getOne:em.getReference() 延迟加载
* 返回的是一个客户的动态代理对象,什么时候用,什么时候查询
*/
@Test
@Transactional
public void testGetOne() {
Customer customer = customerDao.getOne(4L);
System.out.println(customer);
}
与JPA相似,这里也有延迟加载。
使用jpql的方式查询
既然是封装的jpa规范,肯定也能使用jpql。我们首先在dao接口定义方法:
/**
* 根据客户名称查询客户
* 使用jpql的形式查询
* jpql: from Customer where custName = ?
* 配置jpql语句,使用@Query注解
* 也可以是这种形式
*/
@Query(value = "from Customer where custName = :custName")
Customer findJpql(@Param("custName") String custName);
之后使用即可:
public void testFindJPQL(){
Customer customer = customerDao.findJpql("我去");
System.out.println(customer);
}
这里我们注意到,query语句里使用了这样的形式。其实我们也可以使用下面这种形式:
/**
* 根据客户名称和客户id查询客户
* jpql:from Customer where custName = ? and custId = ?
* 可以是这种形式
*
* @param custName
* @param custId
* @return
*/
@Query(value = "from Customer where custName = ?1 and custId = ?2")
Customer findCustNameAndId(String custName, Long custId);
一种是通过索引,一种是通过标注@param后直接在语句中写上:加名字即可
测试更新或删除
更新与删除有点点不同,需要添加一个@Modifying注解:
/**
* 使用jpql完成更新操作
* 根据id更新客户的名称
* sql: update cst_customer set cust_name = ? where cust_id = ?
* jpql:update Customer set custName = ? where custId = ?
* 需要手动添加事务的支持
*
* @modifying 注解以通知这是一个delete或update操作
*/
@Query(value = "update Customer set custName = ?2 where custId = ?1")
@Modifying
void updateCustomer(Long custId, String custName);
编写测试类:
@Test
@Transactional
public void testUpdate(){
customerDao.updateCustomer(2L,"我去");
}
需要加上支持事务的注解。
使用原生sql查询
SpringDataJpa也是支持原生sql查询的,我们在dao中定义方法:
/**
* 以sql的形式查询
* 查询全部的客户
* sql: select * from cst_customer;
* nativeQuery:true:sql查询
* false:jpql查询
*/
@Query(value = "select * from cst_customer", nativeQuery = true)
List<Object[]> findSql();
只要把nativeQuery改成true即可使用原生sql(默认是false,使用jpql)
编写测试类:
/**
* 测试sql查询
*/
@Test
public void testFindSql(){
List<Object[]> list = customerDao.findSql();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
}
类似的,我们也可以使用占位符(?)
/**
* 带参数的模糊查询sql
* @param custName
* @return
*/
@Query(value = "select * from cst_customer where cust_name like ?1", nativeQuery = true)
List<Object[]> findSqlWithParam(String custName);
编写测试类:
/**
* 测试带参数的模糊sql查询
*/
@Test
public void testFindSqWithParam(){
List<Object[]> list = customerDao.findSqlWithParam("我%");
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
}
按照spring官方定义的命名规则的查询
实际上,我们不必自己写jpql语句,只需要按照官方的命名方式命名即可。我上官方文档看了看,目前有这些:
可以看到这些基本包含了所有的查询方法了。我们自己写一个试试:
/**
* 方法名的约定:
* findBy:查询
* 对象中的属性名(首字母大写) :查询的条件
* findByCustName -- 根据客户名称查询 默认情况:使用等于的方式查询
*
* 在springdatajpa的运行阶段会根据方法名称进行解析, findBy from xx(实体类)
* 属性名 where custName =
* @param custName
* @return
*/
Customer findByCustName(String custName);
编写测试类:
/**
* 测试方法命名规则的查询
*/
@Test
public void testFindByCustName(){
Customer customer = customerDao.findByCustName("我去");
System.out.println(customer);
}
其他的还有很多,这里就不再一一测试了,按照表格对应的关系书写即可。
总结
可以看到SpringDataJpa的功能十分强大,几乎涵盖了全部的常用应有场景。但这里并没有涉及到多表的配置。之后会学习如何配置多表。