SpringData JPA学习

SpringData-JPA

SpringData可以结合JPA使用也可以结合JDBC使用

一、SpringData之后HelloWorld

新建一个maven工程

1.pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.springdata.jpa</groupId>
    <artifactId>springdata_jpa</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.3.3.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.3.3.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.common</groupId>
            <artifactId>hibernate-commons-annotations</artifactId>
            <version>5.0.1.Final</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.8.0-beta2</version>
        </dependency>

    </dependencies>
</project>

2.属性配置文件db.properties

jdbc.user=root
jdbc.password=mysql123
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///jpa?characterEncoding=utf8

3.Spring配置文件application.xml

<?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: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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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 组件-->
    <context:component-scan base-package="com.springdata.jpa"></context:component-scan>

    <!--加载属性配置文件-->
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>

    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    </bean>

    <!--配置EntityManagerFactory-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--配置数据源-->
        <property name="dataSource" ref="dataSource"></property>
        <!--配置实体类存放路径-->
        <property name="packagesToScan" value="com.springdata.jpa"></property>
        <!--配置jpa实现产品-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
        </property>

        <!--配置jpa属性-->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>

    </bean>
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <!--管理的对象-->
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
    </bean>

    <!--开启基于注解的事务配置-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

    <!--配置spring-data-->
    <jpa:repositories base-package="com.springdata.jpa"
                      entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>

</beans>
如果报错:EntityPathResolver must not be null!
要配置 包扫描,使扫描到repository
<!--扫描spring 组件-->
<context:component-scan base-package="com.springdata.jpa"></context:component-scan>

4.Entity实体

package com.springdata.jpa.entities;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Entity
@Table(name = "tb_cat")
public class Cat implements Serializable {

    @Id
    @GeneratedValue
    private Integer id;
    @Column(name = "cat_name")
    private String catName;
    @Column(name = "date")
    @Temporal(TemporalType.DATE)
    private Date date;
	// 省略了getter/setter和toString
}

5.Repositoty类

// 不需要手动配置加载到Spring容器中去
// 继承Repository,第一个参数是对应的实体类,第二个参数是对应的主键类型
public interface CatRepository extends Repository<Cat, Integer> {
	// getBy后sin 可以接对象的属性
    Cat getByCatName(String name);

}

6.测试

注:测试前,在数据库中手动添加一条数据。

@Test
public void testHelloWorld() {
    ApplicationContext act = new ClassPathXmlApplicationContext("application.xml");
    CatRepository bean = act.getBean(CatRepository.class);
    Cat aa = bean.getByCatName("aa");
    System.out.println(aa);
}

控制台打印:

Hibernate: 
    select
        cat0_.id as id1_0_,
        cat0_.cat_name as cat_name2_0_,
        cat0_.date as date3_0_ 
    from
        tb_cat cat0_ 
    where
        cat0_.cat_name=?
Cat{id=1, catName='aa', date=2018-10-03}

二、Repository接口

1.继承Repository接口

// 第一个参数是实体对象类,第二个参数是对象的主键类型
public interface DogDao extends Repository<Dog, Integer>{
	
	Dog getByDogName(String dogName);

}

2.@RepositoryDefinition注解

// 第一个属性domainClass的值是实体类对象类,第二个属性的对象的主键类型
@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
	
	Dog getByDogName(String dogName);

}

3.Repository中方法的定义

  • 查询
1.查询方法要以get,find,read开头
2.方法名的定义要依据规范
3.查询条件可以用And或Or连接
3.查询字段用对象实体类属性名代替
4.具体命名规则如下:
KeywordSampleJPQL snippet
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is,EqualsfindByFirstname,findByFirstnameIs,findByFirstnameEquals… where x.firstname = ?1
BetweenfindByStartDateBetween… where x.startDate between ?1 and ?2
LessThanfindByAgeLessThan… where x.age < ?1
LessThanEqualfindByAgeLessThanEqual… where x.age <= ?1
GreaterThanfindByAgeGreaterThan… where x.age > ?1
GreaterThanEqualfindByAgeGreaterThanEqual… where x.age >= ?1
AfterfindByStartDateAfter… where x.startDate > ?1
BeforefindByStartDateBefore… where x.startDate < ?1
IsNullfindByAgeIsNull… where x.age is null
IsNotNull,NotNullfindByAge(Is)NotNull… where x.age not null
LikefindByFirstnameLike… where x.firstname like ?1
NotLikefindByFirstnameNotLike… where x.firstname not like ?1
StartingWithfindByFirstnameStartingWith… where x.firstname like ?1 (parameter bound with appended %)
EndingWithfindByFirstnameEndingWith… where x.firstname like ?1 (parameter bound with prepended %)
ContainingfindByFirstnameContaining… where x.firstname like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByLastnameDesc… where x.age = ?1 order by x.lastname desc
NotfindByLastnameNot… where x.lastname <> ?1
InfindByAgeIn(Collection<Age> ages)… where x.age in ?1
NotInfindByAgeNotIn(Collection<Age> ages)… where x.age not in ?1
TruefindByActiveTrue()… where x.active = true
FalsefindByActiveFalse()… where x.active = false
IgnoreCasefindByFirstnameIgnoreCase… where UPPER(x.firstame) = UPPER(?1)
@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
	
	Dog getByDogName(String dogName);

	List<Dog> getByDogNameLikeAndIdLessThan(String like, Integer id);
	
	List<Dog> findByDogNameStartingWithAndIdGreaterThan(String start, Integer id);
	
	List<Dog> readByDogNameLikeOrId(String name, Integer id);
	
}

4.@Query()注解使用JPQL

@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
	
	Dog getByDogName(String dogName);

	List<Dog> getByDogNameLikeAndIdLessThan(String like, Integer id);
	
	List<Dog> findByDogNameStartingWithAndIdGreaterThan(String start, Integer id);
	
	List<Dog> readByDogNameLikeOrId(String name, Integer id);
	
	// 使用@Query注解可以自定义jpql语句
	@Query("SELECT d1 FROM Dog d1 WHERE d1.id =(SELECT MAX(d2.id) FROM Dog d2)")
	Dog selectDogByMaxId(); // 方法命名可以不用spring data jpa提供的命名规范了
}

5.@Query传参

1.占位符顺序传参

@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
	// 使用@Query注解可以自定义jpql语句
	@Query("SELECT d1 FROM Dog d1 WHERE d1.id =(SELECT MAX(d2.id) FROM Dog d2)")
	Dog selectDogByMaxId();
	
    // 写占位符?的时候,一定要指定数字,这个数字代表这个占位符接收的是方法中对应的第几个参数 
	@Query("SELECT d FROM Dog d WHERE d.id = ?2 and d.dogName = ?1")
	List<Dog> selectDogByDogNameAndId(String dogName, Integer id);
	
	
}

2.命名参数传参
@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
	
	// 使用@Query注解可以自定义jpql语句
	@Query("SELECT d1 FROM Dog d1 WHERE d1.id =(SELECT MAX(d2.id) FROM Dog d2)")
	Dog selectDogByMaxId();
	
	@Query("SELECT d FROM Dog d WHERE d.id = ?2 and d.dogName = ?1")
	List<Dog> selectDogByDogNameAndId(String dogName, Integer id);
	
	// 使用 :参数名的方式,指定要接收的参数的命名,在方法的参数列表位置通过@Param参数指定对应的参数的命名,
	// 当两个参数命名一致时,才能写入值
	@Query("SELECT d FROM Dog d WHERE d.id = :id and d.dogName = :dogName")
	List<Dog> selectDogsByNameAndId(@Param("dogName") String name, @Param("id")Integer id);
	
	
}

3.关于模糊查询

方式一:

// 这个方法进行模糊查询的时候,要传入%号
@Query("FROM Dog d WHERE d.dogName like ?1")
public List<Dog> getDogByNameLike(String like);

// 方法的使用,传入的参数要带上%
List<Dog> dogs = dao.getDogByNameLike("%a%");

方式二:

// 这里%也可以写在参数位置
@Query("FROM Dog d WHERE d.dogName like %:name")
public List<Dog> getDogsByNameLike(@Param("name") String dogName);
	
// 方法的使用,参数位置可以不传入%
List<Dog> dogs = dao.getDogsByNameLike("a");

6.@Query注解写SQL查询

// 在@Query注解中设置nativeQuery属性为true
@Query(value = "SELECT COUNT(1) FROM tb_dog", nativeQuery = true)
Integer getCount();
// 参数可以写成占位符的形式,方法中传参,依据占位符的顺序
@Query(value = "select * from tb_dog where id = ? and dog_name = ?", nativeQuery = true)
Dog getDogById(Integer id, String name);

7.@Modifying修改和删除

修改:

@RepositoryDefinition(domainClass = Person.class, idClass = Integer.class)
public interface PersonDao {
	
	@Query("FROM Person p WHERE p.id = ?1")
	Person getPersonById(Integer id);
	
	@Query(value = "SELECT * FROM tb_person WHERE last_name = ?", nativeQuery = true)
	Person getPersonByLastName(String lastName);
	
    // 使用修改和删除时,使用@Modifying+@Query注解
	@Modifying
	@Query("UPDATE Person p SET p.lastName = :lastName  WHERE p.id = :id")
	int updateLastNameById(@Param("lastName")String lastName, @Param("id")Integer id);

}

如果直接调用这个Repository中的方法会报错!

org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query

修改和删除操作时,必须要有事务!

所以修改代码,新建一个PersonService类,在PersonService中使用PersonDao方法,并添加事务!

@Service // 加入到Spring容器中,注意配置包扫描
public class PersonService {
	
	private PersonDao personDao;
	
	// 在构造器位置,personDao可以自动注入,类似于@Autowired
	public PersonService(PersonDao personDao) {
		this.personDao = personDao;
	}
	
	@Transactional // 添加事务支持
	public void updatePerson(String lastName, Integer id) {
		int i = personDao.updateLastNameById(lastName, id);
		System.out.println(i);
	}

}

测试:

@Test
public void testUpdate() {
    PersonService personService = act.getBean(PersonService.class);
    personService.updatePerson("小明", 2);
}

删除:

@RepositoryDefinition(domainClass = Person.class, idClass = Integer.class)
public interface PersonDao {
	
	@Query("FROM Person p WHERE p.id = ?1")
	Person getPersonById(Integer id);
	
	@Query(value = "SELECT * FROM tb_person WHERE last_name = ?", nativeQuery = true)
	Person getPersonByLastName(String lastName);
	
	@Modifying
	@Query("UPDATE Person p SET p.lastName = :lastName  WHERE p.id = :id")
	int updateLastNameById(@Param("lastName")String lastName, @Param("id")Integer id);
	
	// 使用@Modifying和@Query注解完成删除操作
	@Modifying
	@Query(value = "DELETE FROM tb_person WHERE id = ?", nativeQuery = true)
	int deleteById(Integer id);

}

service中使用添加事务支持:

@Service // 加入到Spring容器中,注意配置包扫描
public class PersonService {
	
	private PersonDao personDao;
	
	// 在构造器位置,personDao可以自动注入,类似于@Autowired
	public PersonService(PersonDao personDao) {
		this.personDao = personDao;
	}
	
	@Transactional // 添加事务支持
	public void updatePerson(String lastName, Integer id) {
		int i = personDao.updateLastNameById(lastName, id);
		System.out.println(i);
	}
	
	@Transactional // 添加事务支持
	public void deletePerson(Integer id) {
		int i = personDao.deleteById(id);
		System.out.println(i);
	}

}

测试:

@Test
public void testDelete() {
    PersonService personService = act.getBean(PersonService.class);
    personService.deletePerson(1);
}

三、CrudRepository

CrudRepository接口中定义了一些能用的增删改查方法

// 继承CrudRepository
public interface PersonRepository extends CrudRepository<Person, Integer>{

}
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
	<S extends T> S save(S entity); //保存单个对象方法
	<S extends T> Iterable<S> saveAll(Iterable<S> entities); //批量保存方法
	Optional<T> findById(ID id); // 查找方法
	boolean existsById(ID id); // 判断是否存在
	Iterable<T> findAll();	// 查找所有
	Iterable<T> findAllById(Iterable<ID> ids); // 通过id查找所有
	long count(); // 获取数据条数
	void deleteById(ID id); // 通过id删除
	void delete(T entity); // 通过实体对象删除
	void deleteAll(Iterable<? ext ends T> entities); // 批量删除
	void deleteAll();  // 删除所有
} 

测试:

@Test
	public void findAllById() {
		PersonRepository personRepository = act.getBean(PersonRepository.class);
        // findAllById()
		Iterable<Person> res = personRepository.findAllById(Arrays.asList(1,2,3,4,5));
		System.out.println(res);
	}

四、PagingAndSortingRepository接口

// 继承可实现分页和排序 
public interface PersonPagingAndSortingRepository extends PagingAndSortingRepository<Person, Integer> {

}
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
	// 排序
	Iterable<T> findAll(Sort sort);
	// 分页
	Page<T> findAll(Pageable pageable);
}

使用方法:

分页:

@Test
public void testPageAndSort() {
    PersonPagingAndSortingRepository dao = act.getBean(PersonPagingAndSortingRepository.class);
    PageRequest pageable = new PageRequest(0, 2);
    Page<Person> page = dao.findAll(pageable);
    System.out.println(page.getNumber()); // 当前页 页码从0开始计算的
    System.out.println(page.getNumberOfElements()); // 总页数
    System.out.println(page.getSize()); // 每页大小
    System.out.println(page.getContent()); // 每页内容
}

五.JpaRepository

// 接口继承JpaRepository
public interface CustomerDao extends JpaRepository<Customer, Integer> {
}
// JpaRepository是PagingAndSortingRepository,
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
	List<T> findAll(); 
	List<T> findAll(Sort sort);
	List<T> findAllById(Iterable<ID> ids);
	<S extends T> List<S> saveAll(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);
}

测试:

@Test
public void test() {
    Customer customer = new Customer();
    customer.setLastName("小芳");
    customer.setBirthDay(new Date());
    customer.setId(1);
    // saveAndFlush()方法,存在就更新,不存在就插入
    customerDao.saveAndFlush(customer);
}

六、带条件的分页JpaSpecificationExecutor

public interface CustomerDao extends JpaRepository<Customer, Integer>, JpaSpecificationExecutor<Customer> {

}

使用:

@Test
public void test02() {
    Specification<Customer> specification = new Specification<Customer>() {
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
            // 构造比较查询条件
            Path path = root.get("id");
            Predicate predicate = criteriaBuilder.gt(path, 0);
            return predicate;
        }
    };
    List<Customer> all = customerDao.findAll(specification);
    System.out.println(all);
}
@Test
    public void test02() {
        Specification<Customer> specification = new Specification<Customer>() {
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Path<String> lastName = root.get("lastName");
                Predicate predicate = criteriaBuilder.like(lastName, "%小");
                Path<Integer> id = root.get("id");
                Predicate predicate1 = criteriaBuilder.equal(id, 2);
                // 两个条件and连接
                query.where(predicate, predicate1);
                return null;
            }
        };


        List<Customer> all = customerDao.findAll(specification);
        System.out.println(all);
    }

七、自定义Repository

  • 1.编写接口,继承Spring Data提供的Repository接口
public interface CustomerDao extends JpaRepository<Customer, Integer>,
        JpaSpecificationExecutor<Customer> {
}
  • 2.自定义接口,编写要实现的方法
public interface CustomerRepository {
	// 自已在接口中定义的方法
    public void test();
}
  • 3.编写步骤2实现类,实现类的命名必须是步骤1的接口名+Impl
// 继承自定义接口
public class CustomerDaoImpl implements CustomerRepository {
	// 可以使用EntityManager
    @PersistenceContext
    private EntityManager entityManager;
	// 实现方法
    public void test() {
        Customer customer = entityManager.find(Customer.class, 2);
        System.out.println(customer);
    }
}
  • 4.修改步骤1接口,使继承自定义接口
public interface CustomerDao extends JpaRepository<Customer, Integer>,
        JpaSpecificationExecutor<Customer>,
		//继承自定义接口
		CustomerRepository {

}

测试:

 @Test
public void repository() {
    customerDao.test();
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值