初入spring boot(七 )Spring Data JPA
Spring Data JPA通过提供基于JPA的Repository极大地减少JPA作为数据访问方案的代码量。
1.定义数据访问层
使用Spring Data JPA建立数据访问层十分简单,只需定义一个继承JpaRepository的接口即可,接口如下:
@RepositoryRestResource(path = "people")
public interface PersonRepository extends JpaRepository<Person, Long> { @RestResource(path = "nameStartsWith", rel = "nameStartsWith")
Person findByNameStartsWith(@Param("name")String name); }
继承JpaRepository接口意味着我们默认已经有了下面的数据访问操作方法:
@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll(); List<T> findAll(Sort var1); List<T> findAll(Iterable<ID> var1); <S extends T> List<S> save(Iterable<S> var1); void flush(); <S extends T> S saveAndFlush(S var1); void deleteInBatch(Iterable<T> var1); void deleteAllInBatch(); T getOne(ID var1); <S extends T> List<S> findAll(Example<S> var1); <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
2.配置使用Spring Data JPA
在Spring环境中,使用Spring Data JPA可通过@EnableJpaRepositories注解来开启Spring Data JPA的支持,@EnableJpaRepositories接收的value参数用来扫描数据访问层所在包下的数据访问的接口定义。
@Configuration
@EnableJpaRepositories("com.test.dao")
public class JpaConfiguration {
@Bean
public EntityManagerFactory entityManagerFactory(){
//...
return null;
} //还需配置DataSource、PlatformTransactionManager等相关必须bean
}
3.定义查询方法
(1)根据属性名查询
1)常规查询。根据属性名来定义查询方法
public interface PersonRepository extends CustomRepository<Person, Long> { /**
* 通过名字相等查询,参数name
* 相当于JPQL:select p from Person p where p.name=?
*/
List<Person> findByName(String name); /**
* 通过名字like查询,参数为name
* 相当于JPQL:select p from Person p where p.name like ?
*/
List<Person> findByNameLike(String name); /**
* 通过名字和地址查询,参数为name和address
* 相当于JPQL:select p from Person p where p.name = ? and p.address = ?
*/
List<Pserson> findByNameAndAddress(String name,String address);
}
从代码可以看出,这里使用了findBy、like、And这样的关键字。其中findBy可以用find、read、readBy、query、queryBy、get、getBy来代替。
2)限制结果数量。结果数量是用top和first关键字来实现的:\
public interface PersonRepository extends CustomRepository<Person, Long> { /**
* 获取查询条件的前10条数据
* 通过名字相等查询,参数name
* 相当于JPQL:select p from Person p where p.name=?
*/
List<Person> findFirst10ByName(String name); /**
* 获取查询条件的前10条数据
* 通过名字like查询,参数为name
* 相当于JPQL:select p from Person p where p.name like ?
*/
List<Person> findTop10ByNameLike(String name); }
(2)使用JPA的NamedQuery查询
Spring Data JPA支持用JPA的NameQuery来定义查询方法,即一个名称映射一个查询语句。
@Entity
@NamedQuery(name = "Person.withNameAndAddressNamedQuery",
query = "select p from Person p where p.name=? and address=?")
public class Person {
@Id
@GeneratedValue
private Long id; private String name; private Integer age; private String address; public Person() {
super();
} public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
} }
这时在接口里使用 Person withNameAndAddressNamedQuery(String name,String address); 时是使用的在上面定义的sql语句,而不是根据方法名称查询
(3)使用@Query查询
1)使用命名参数,使用名称来匹配查询参数。Spring Data JPA还支持@Query注解在接口的方法上实现查询
@Query("select p from Person p where p.name= :name and p.address= :address")
Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);
2)使用参数索引
@Query("select p from Person p where p.name= ? and p.address= ?")
Person withNameAndAddressNamedQuery(String name,String address);
3)更新查询。Spring Data JPA支持@Modifying和@Query注解组合事件来更新查询
(4)JPA提供了基于准则查询方式,即Criteria查询。而Spring Data JPA提供了一个Specification(规范)接口让我们可以更方便的构造准则查询,Specification接口定义了一个toPredicate方法用来构造查询条件。
(5)自定义Repository的实现
spring data提供了CrudRepository和PagingAndSortingRepository,spring data JPA也提供了JpaRepository。如果我们想把自己常用的数据库操作封装起来,像JpaRepository一样提供给我们领域类的Repository接口使用,应该怎么做?
1)定义自定义Repository接口
@NoRepositoryBean
public interface CustomRepository<T, ID extends Serializable>extends JpaRepository<T, ID> ,JpaSpecificationExecutor<T>{ Page<T> findByAuto(T example,Pageable pageable); }
1. @NoRepositoryBean指明当前这个接口不是我们领域类的接口(例如PersonRepository)
2. 我们自定义的Repository实现JpaRepository接口(这里也可以实现PagingAndSortingRepository接口,看具体需求),具备JpaRepository的能力
3. 要定义的数据操作方法在接口中的定义
2)定义接口实现
public class CustomRepositoryImpl <T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements CustomRepository<T,ID> { private final EntityManager entityManager; public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
} @Override
public Page<T> findByAuto(T example, Pageable pageable) {
return findAll(byAuto(entityManager, example),pageable); //在此处定义数据访问操作
} } 1. 首先要实现CustomRepository接口,继承SimpleJpaRepository类让我们可以使用其提供的方法(例如:findAll)
2. 让数据库操作方法中可以使用entityManager
3. CustomRepositoryImpl的构造函数,需当前处理的领域类类型和entityManager作为构造函数
3)自定义RepositoryFactoryBean。自定义JpaRepositoryFactoryBean替代默认RepositoryFactoryBean,我们会获得一个RepositoryFactory,RepositoryFactory将会注册我们自定义的Repository的实现
public class CustomRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable>
extends JpaRepositoryFactoryBean<T, S, ID> {// @Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {//
return new CustomRepositoryFactory(entityManager);
} private static class CustomRepositoryFactory extends JpaRepositoryFactory {// public CustomRepositoryFactory(EntityManager entityManager) {
super(entityManager);
} @Override
@SuppressWarnings({"unchecked"})
protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(
RepositoryInformation information, EntityManager entityManager) {//
return new CustomRepositoryImpl<T, ID>((Class<T>) information.getDomainType(), entityManager); } @Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {//
return CustomRepositoryImpl.class;
}
}
} 1. 自定义RepositoryFactoryBean,继承JpaRepositoryFactoryBean
2. 重写createRepositoryFactory方法,用当前的CustomRepositoryFactory创建实例
3. 创建CustomRepositoryFactory,并继承JpaRepositoryFactory
4. 重写getTargetRepository方法,获得当前自定义的Repository实现
5. 重写getRepositoryBaseClass,获得当前自定义的Repository实现的类型
4)开启自定义支持使用@EnableJpaRepositories的repositoryFactoryBeanClass来指定FactoryBean即可
@SpringBootApplication
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class TestApplication {
@Autowired
PersonRepository personRepository; public static void main(String[] args) {
SpringApplication.run(Ch82Application.class, args); }
}
Spring boot的支持
1. JDBC的自动配置
spring-boot-starter-data-jpa依赖于spring-boot-starter-jdbc,而Spring Boot对JDBC做了一些自动配置。源码放置在org.springframework.boot.autoconfigure.jdbc下,如图
从源码分析可以看出,我们通过“spring.datasource”为前缀的属性自动配置dataSource,Spring Boot自动开启了注解事务的支持(@EnableTransactionManagement);还配置了一个jdbcTemplate。Spring Boot还提供了一个初始化数据的功能:放置在类路径下的schema.sql文件会自动用来初始化表结构;放置在类路径下的data.sql文件会自动用来填充表数据。
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.boot.autoconfigure.jdbc; import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID; import javax.sql.DataSource; import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; /**
* Base class for configuration of a data source.
*
* @author Dave Syer
* @author Maciej Walkowiak
* @author Stephane Nicoll
* @author Benedikt Ritter
* @author Eddú Meléndez
* @since 1.1.0
*/
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties
implements BeanClassLoaderAware, EnvironmentAware, InitializingBean { private ClassLoader classLoader; private Environment environment; /**
* Name of the datasource.
*/
private String name = "testdb"; /**
* Generate a random datasource name.
*/
private boolean generateUniqueName; /**
* Fully qualified name of the connection pool implementation to use. By default, it
* is auto-detected from the classpath.
*/
private Class<? extends DataSource> type; /**
* Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
*/
private String driverClassName; /**
* JDBC url of the database.
*/
private String url; /**
* Login user of the database.
*/
private String username; /**
* Login password of the database.
*/
private String password; /**
* JNDI location of the datasource. Class, url, username & password are ignored when
* set.
*/
private String jndiName; /**
* Populate the database using 'data.sql'.
*/
private boolean initialize = true; /**
* Platform to use in the schema resource (schema-${platform}.sql).
*/
private String platform = "all"; /**
* Schema (DDL) script resource references.
*/
private List<String> schema; /**
* User of the database to execute DDL scripts (if different).
*/
private String schemaUsername; /**
* Password of the database to execute DDL scripts (if different).
*/
private String schemaPassword; /**
* Data (DML) script resource references.
*/
private List<String> data; /**
* User of the database to execute DML scripts.
*/
private String dataUsername; /**
* Password of the database to execute DML scripts.
*/
private String dataPassword; /**
* Do not stop if an error occurs while initializing the database.
*/
private boolean continueOnError = false; /**
* Statement separator in SQL initialization scripts.
*/
private String separator = ";"; /**
* SQL scripts encoding.
*/
private Charset sqlScriptEncoding; private EmbeddedDatabaseConnection embeddedDatabaseConnection = EmbeddedDatabaseConnection.NONE; private Xa xa = new Xa(); private String uniqueName; @Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
} @Override
public void setEnvironment(Environment environment) {
this.environment = environment;
} @Override
public void afterPropertiesSet() throws Exception {
this.embeddedDatabaseConnection = EmbeddedDatabaseConnection
.get(this.classLoader);
} /**
* Initialize a {@link DataSourceBuilder} with the state of this instance.
* @return a {@link DataSourceBuilder} initialized with the customizations defined on
* this instance
*/
public DataSourceBuilder initializeDataSourceBuilder() {
return DataSourceBuilder.create(getClassLoader()).type(getType())
.driverClassName(determineDriverClassName()).url(determineUrl())
.username(determineUsername()).password(determinePassword());
} public String getName() {
return this.name;
} public void setName(String name) {
this.name = name;
} public boolean isGenerateUniqueName() {
return this.generateUniqueName;
} public void setGenerateUniqueName(boolean generateUniqueName) {
this.generateUniqueName = generateUniqueName;
} public Class<? extends DataSource> getType() {
return this.type;
} public void setType(Class<? extends DataSource> type) {
this.type = type;
} /**
* Return the configured driver or {@code null} if none was configured.
* @return the configured driver
* @see #determineDriverClassName()
*/
public String getDriverClassName() {
return this.driverClassName;
} public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
} /**
* Determine the driver to use based on this configuration and the environment.
* @return the driver to use
* @since 1.4.0
*/
public String determineDriverClassName() {
if (StringUtils.hasText(this.driverClassName)) {
Assert.state(driverClassIsLoadable(),
"Cannot load driver class: " + this.driverClassName);
return this.driverClassName;
}
String driverClassName = null; if (StringUtils.hasText(this.url)) {
driverClassName = DatabaseDriver.fromJdbcUrl(this.url).getDriverClassName();
} if (!StringUtils.hasText(driverClassName)) {
driverClassName = this.embeddedDatabaseConnection.getDriverClassName();
} if (!StringUtils.hasText(driverClassName)) {
throw new DataSourceBeanCreationException(this.embeddedDatabaseConnection,
this.environment, "driver class");
}
return driverClassName;
} private boolean driverClassIsLoadable() {
try {
ClassUtils.forName(this.driverClassName, null);
return true;
}
catch (UnsupportedClassVersionError ex) {
// Driver library has been compiled with a later JDK, propagate error
throw ex;
}
catch (Throwable ex) {
return false;
}
} /**
* Return the configured url or {@code null} if none was configured.
* @return the configured url
* @see #determineUrl()
*/
public String getUrl() {
return this.url;
} public void setUrl(String url) {
this.url = url;
} /**
* Determine the url to use based on this configuration and the environment.
* @return the url to use
* @since 1.4.0
*/
public String determineUrl() {
if (StringUtils.hasText(this.url)) {
return this.url;
}
String url = this.embeddedDatabaseConnection.getUrl(determineDatabaseName());
if (!StringUtils.hasText(url)) {
throw new DataSourceBeanCreationException(this.embeddedDatabaseConnection,
this.environment, "url");
}
return url;
} private String determineDatabaseName() {
if (this.generateUniqueName) {
if (this.uniqueName == null) {
this.uniqueName = UUID.randomUUID().toString();
}
return this.uniqueName;
}
return this.name;
} /**
* Return the configured username or {@code null} if none was configured.
* @return the configured username
* @see #determineUsername()
*/
public String getUsername() {
return this.username;
} public void setUsername(String username) {
this.username = username;
} /**
* Determine the username to use based on this configuration and the environment.
* @return the username to use
* @since 1.4.0
*/
public String determineUsername() {
if (StringUtils.hasText(this.username)) {
return this.username;
}
if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName())) {
return "sa";
}
return null;
} /**
* Return the configured password or {@code null} if none was configured.
* @return the configured password
* @see #determinePassword()
*/
public String getPassword() {
return this.password;
} public void setPassword(String password) {
this.password = password;
} /**
* Determine the password to use based on this configuration and the environment.
* @return the password to use
* @since 1.4.0
*/
public String determinePassword() {
if (StringUtils.hasText(this.password)) {
return this.password;
}
if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName())) {
return "";
}
return null;
} public String getJndiName() {
return this.jndiName;
} /**
* Allows the DataSource to be managed by the container and obtained via JNDI. The
* {@code URL}, {@code driverClassName}, {@code username} and {@code password} fields
* will be ignored when using JNDI lookups.
* @param jndiName the JNDI name
*/
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
} public boolean isInitialize() {
return this.initialize;
} public void setInitialize(boolean initialize) {
this.initialize = initialize;
} public String getPlatform() {
return this.platform;
} public void setPlatform(String platform) {
this.platform = platform;
} public List<String> getSchema() {
return this.schema;
} public void setSchema(List<String> schema) {
this.schema = schema;
} public String getSchemaUsername() {
return this.schemaUsername;
} public void setSchemaUsername(String schemaUsername) {
this.schemaUsername = schemaUsername;
} public String getSchemaPassword() {
return this.schemaPassword;
} public void setSchemaPassword(String schemaPassword) {
this.schemaPassword = schemaPassword;
} public List<String> getData() {
return this.data;
} public void setData(List<String> data) {
this.data = data;
} public String getDataUsername() {
return this.dataUsername;
} public void setDataUsername(String dataUsername) {
this.dataUsername = dataUsername;
} public String getDataPassword() {
return this.dataPassword;
} public void setDataPassword(String dataPassword) {
this.dataPassword = dataPassword;
} public boolean isContinueOnError() {
return this.continueOnError;
} public void setContinueOnError(boolean continueOnError) {
this.continueOnError = continueOnError;
} public String getSeparator() {
return this.separator;
} public void setSeparator(String separator) {
this.separator = separator;
} public Charset getSqlScriptEncoding() {
return this.sqlScriptEncoding;
} public void setSqlScriptEncoding(Charset sqlScriptEncoding) {
this.sqlScriptEncoding = sqlScriptEncoding;
} public ClassLoader getClassLoader() {
return this.classLoader;
} public Xa getXa() {
return this.xa;
} public void setXa(Xa xa) {
this.xa = xa;
} /**
* XA Specific datasource settings.
*/
public static class Xa { /**
* XA datasource fully qualified name.
*/
private String dataSourceClassName; /**
* Properties to pass to the XA data source.
*/
private Map<String, String> properties = new LinkedHashMap<String, String>(); public String getDataSourceClassName() {
return this.dataSourceClassName;
} public void setDataSourceClassName(String dataSourceClassName) {
this.dataSourceClassName = dataSourceClassName;
} public Map<String, String> getProperties() {
return this.properties;
} public void setProperties(Map<String, String> properties) {
this.properties = properties;
} } static class DataSourceBeanCreationException extends BeanCreationException { DataSourceBeanCreationException(EmbeddedDatabaseConnection connection,
Environment environment, String property) {
super(getMessage(connection, environment, property));
} private static String getMessage(EmbeddedDatabaseConnection connection,
Environment environment, String property) {
StringBuilder message = new StringBuilder();
message.append("Cannot determine embedded database " + property
+ " for database type " + connection + ". ");
message.append("If you want an embedded database please put a supported "
+ "one on the classpath. ");
message.append("If you have database settings to be loaded from a "
+ "particular profile you may need to active it");
if (environment != null) {
String[] profiles = environment.getActiveProfiles();
if (ObjectUtils.isEmpty(profiles)) {
message.append(" (no profiles are currently active)");
}
else {
message.append(" (the profiles \""
+ StringUtils.arrayToCommaDelimitedString(
environment.getActiveProfiles())
+ "\" are currently active)"); }
}
message.append(".");
return message.toString();
}
}
}
2.对JPA的自动配置
Spring Boot对JPA的自动配置放置在org.springframework.boot.autoconfigure.orm.jpa下,如图
从HibernateJpaAutoConfiguration可以看出,Spring boot默认JPA的实现者是Hibernate;HibernateJpaAutoConfiguration依赖于DataSourceAutoConfiguration。
@Configuration
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class,
EnableTransactionManagement.class, EntityManager.class })
@Conditional(HibernateEntityManagerCondition.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class })
public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration { private static final Log logger = LogFactory
.getLog(HibernateJpaAutoConfiguration.class); private static final String JTA_PLATFORM = "hibernate.transaction.jta.platform"; /**
* {@code NoJtaPlatform} implementations for various Hibernate versions.
*/
private static final String[] NO_JTA_PLATFORM_CLASSES = {
"org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform",
"org.hibernate.service.jta.platform.internal.NoJtaPlatform" }; /**
* {@code WebSphereExtendedJtaPlatform} implementations for various Hibernate
* versions.
*/
private static final String[] WEBSPHERE_JTA_PLATFORM_CLASSES = {
"org.hibernate.engine.transaction.jta.platform.internal.WebSphereExtendedJtaPlatform",
"org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform", }; public HibernateJpaAutoConfiguration(DataSource dataSource,
JpaProperties jpaProperties,
ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
super(dataSource, jpaProperties, jtaTransactionManager,
transactionManagerCustomizers);
} @Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
} @Override
protected Map<String, Object> getVendorProperties() {
Map<String, Object> vendorProperties = new LinkedHashMap<String, Object>();
vendorProperties.putAll(getProperties().getHibernateProperties(getDataSource()));
return vendorProperties;
} @Override
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
super.customizeVendorProperties(vendorProperties);
if (!vendorProperties.containsKey(JTA_PLATFORM)) {
configureJtaPlatform(vendorProperties);
}
} private void configureJtaPlatform(Map<String, Object> vendorProperties)
throws LinkageError {
JtaTransactionManager jtaTransactionManager = getJtaTransactionManager();
if (jtaTransactionManager != null) {
if (runningOnWebSphere()) {
// We can never use SpringJtaPlatform on WebSphere as
// WebSphereUowTransactionManager has a null TransactionManager
// which will cause Hibernate to NPE
configureWebSphereTransactionPlatform(vendorProperties);
}
else {
configureSpringJtaPlatform(vendorProperties, jtaTransactionManager);
}
}
else {
vendorProperties.put(JTA_PLATFORM, getNoJtaPlatformManager());
}
} private boolean runningOnWebSphere() {
return ClassUtils.isPresent(
"com.ibm.websphere.jtaextensions." + "ExtendedJTATransaction",
getClass().getClassLoader());
} private void configureWebSphereTransactionPlatform(
Map<String, Object> vendorProperties) {
vendorProperties.put(JTA_PLATFORM, getWebSphereJtaPlatformManager());
} private Object getWebSphereJtaPlatformManager() {
return getJtaPlatformManager(WEBSPHERE_JTA_PLATFORM_CLASSES);
} private void configureSpringJtaPlatform(Map<String, Object> vendorProperties,
JtaTransactionManager jtaTransactionManager) {
try {
vendorProperties.put(JTA_PLATFORM,
new SpringJtaPlatform(jtaTransactionManager));
}
catch (LinkageError ex) {
// NoClassDefFoundError can happen if Hibernate 4.2 is used and some
// containers (e.g. JBoss EAP 6) wraps it in the superclass LinkageError
if (!isUsingJndi()) {
throw new IllegalStateException("Unable to set Hibernate JTA "
+ "platform, are you using the correct "
+ "version of Hibernate?", ex);
}
// Assume that Hibernate will use JNDI
if (logger.isDebugEnabled()) {
logger.debug("Unable to set Hibernate JTA platform : " + ex.getMessage());
}
}
} private boolean isUsingJndi() {
try {
return JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable();
}
catch (Error ex) {
return false;
}
} private Object getNoJtaPlatformManager() {
return getJtaPlatformManager(NO_JTA_PLATFORM_CLASSES);
} private Object getJtaPlatformManager(String[] candidates) {
for (String candidate : candidates) {
try {
return Class.forName(candidate).newInstance();
}
catch (Exception ex) {
// Continue searching
}
}
throw new IllegalStateException("Could not configure JTA platform");
} @Order(Ordered.HIGHEST_PRECEDENCE + 20)
static class HibernateEntityManagerCondition extends SpringBootCondition { private static String[] CLASS_NAMES = {
"org.hibernate.ejb.HibernateEntityManager",
"org.hibernate.jpa.HibernateEntityManager" }; @Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("HibernateEntityManager");
for (String className : CLASS_NAMES) {
if (ClassUtils.isPresent(className, context.getClassLoader())) {
return ConditionOutcome
.match(message.found("class").items(Style.QUOTE, className));
}
}
return ConditionOutcome.noMatch(message.didNotFind("class", "classes")
.items(Style.QUOTE, Arrays.asList(CLASS_NAMES)));
} } }
从JpaProperties的源码可以看出,配置JPA可以使用spring.jpa为前缀的属性在application.properties中配置。
从JpaBaseConfiguration的源码中可以看出,Spring boot为我们配置了transactionManager、jpaVendorAdapter、entityManagerFactory等Bean。JpaBaseConfiguration还有一个getPackagesToScan方法,可以自动扫描注解有Entity的实体类。
在web项目中我们经常会遇到在控制器或者页面访问数据的时候出现会话连接已关闭的错误,这个时候我们会配置一个Open EntityManager(Session)In View这个过滤器。令人惊喜的是,Spring boot为我们自动配置了OpenEntityManagerInViewIntercept这个bean,并注册到Spring mvc的拦截器中。
3. 对Spring Data JPA的自动配置
而Spring boot对Spring Data Jpa的自动配置放置在org.springframework.boot.autoconfigure.data.jpa下,如图
从JpaRepositoriesAutoConfiguration和JPARepositoriesAutoConfigureRegistrar源码可以看出,JpaRepositoriesAutoConfiguration是依赖于HibernateJpaAutoConfiguration配置的,且Spring boot自动开启了对spring data jpa的支持,即我们无须在配置类中显示声明@EnableJpaRepositories.
总结 : 通过上面的分析,在Spring boot下使用Spring Data JPA,在项目的maven依赖里添加spring-boot-starter-data-jpa,然后只需定义DataSource、实体类和数据访问层,并在需要使用数据访问的地方注入数据访问层的Bean即可,无须任何额外配置。
初入spring boot(七 )Spring Data JPA的更多相关文章
- spring boot系列(五)spring boot 配置spring data jpa (查询方法)
接着上面spring boot系列(四)spring boot 配置spring data jpa 保存修改方法继续做查询的测试: 1 创建UserInfo实体类,代码和https://www.cnb ...
- Spring Boot 整合Spring Data JPA
Spring Boot整合Spring Data JPA 1)加入依赖 <dependency> <groupId>org.springframework.boot</g ...
- Spring Boot 之Spring data JPA简介
文章目录 添加依赖 添加entity bean 创建 Dao Spring Data Configuration 测试 Spring Boot 之Spring data JPA简介 JPA的全称是Ja ...
- Spring Boot 结合Spring Data结合小项目(增,删,查,模糊查询,分页,排序)
本次做的小项目是类似于,公司发布招聘信息,因此有俩个表,一个公司表,一个招聘信息表,俩个表是一对多的关系 项目整体结构: Spring Boot和Spring Data结合的资源文件 applicat ...
- Spring Boot中Spring data注解的使用
文章目录 Spring Data Annotations @Transactional @NoRepositoryBean @Param @Id @Transient @CreatedBy, @Las ...
- Spring Boot集成Spring Data Reids和Spring Session实现Session共享
首先,需要先集成Redis的支持,参考:http://www.cnblogs.com/EasonJim/p/7805665.html Spring Boot集成Spring Data Redis+Sp ...
- spring boot 集成 Mybatis,JPA
相对应MyBatis, JPA可能大家会比较陌生,它并不是一个框架,而是一组规范,其使用跟Hibernate 差不多,原理层面的东西就不多讲了,主要的是应用. Mybatis就不多说了,SSM这三个框 ...
- 一:Spring Boot、Spring Cloud
上次写了一篇文章叫Spring Cloud在国内中小型公司能用起来吗?介绍了Spring Cloud是否能在中小公司使用起来,这篇文章是它的姊妹篇.其实我们在这条路上已经走了一年多,从16年初到现在. ...
- 基于Spring Boot、Spring Cloud、Docker的微服务系统架构实践
由于最近公司业务需要,需要搭建基于Spring Cloud的微服务系统.遍访各大搜索引擎,发现国内资料少之又少,也难怪,国内Dubbo正统治着天下.但是,一个技术总有它的瓶颈,Dubbo也有它捉襟见肘 ...
- spring boot 与 spring cloud 关系
公司使用spring cloud,所以稍微了解一下 看了一下spring官网对 spring boot 以及 spring cloud 的解释 Spring Boot Spring Boot make ...
随机推荐
- Django系列
1.Django框架 2.Django restframework 其他 django之contenttype
- 剑指Offer——平衡二叉树
题目描述: 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 分析: 平衡二叉树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质 ...
- ntopng基础
当你在本地网络监控网络流量,根据流量大小.监控平台/接口.数据库类型等等,可以有许多不同的选择.ntopng是一套开源(遵循GPLv3协议)网络流量分析解决方案,提供基于web界面的实时网络流量监控. ...
- Javascript闭包学习(Closure)
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 下面就是我的学习笔记,对于Javascript初学者应该是很有用的. 一.变量的作用域 要理解 ...
- python 递归深度优先搜索与广度优先搜索算法模拟实现
一.递归原理小案例分析 (1)# 概述 递归:即一个函数调用了自身,即实现了递归 凡是循环能做到的事,递归一般都能做到! (2)# 写递归的过程 1.写出临界条件2.找出这一次和上一次关系3.假设当前 ...
- 【我的Android进阶之旅】Android 混淆文件资源分类整理
之前将所有的混淆都配置在一个 proguard-rules.pro 这个Android Studio新建项目时自动生成的文件里面,而随着项目功能迭代越来越多,代码量越来越多,引用的第二方库.第三方库都 ...
- error: Error: No resource found for attribute ‘layout_scrollFlags’ in package‘包名’
遇到error: Error: No resource found for attribute 'layout_scrollFlags' in package'包名' 这个问题时候刚開始自己也是感觉到 ...
- rtsp/rtmp/hls/onvif测试源以及ffmpeg在流媒体方面的应用
一.rtsp/rtmp/hls/onvif测试源 1. rtsp rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov 2.rtmp rtmp://l ...
- 手把手教你发布自己的 Composer 包
一.前言 Composer 是 PHP 用来管理依赖(dependency)关系的工具.我们不仅要学会使用别人提供的包,更要学会制作和分享自己的软件包,下面演示如何创建一个自己的 Composer 包 ...
- PAT 1093 Count PAT's[比较]
1093 Count PAT's (25 分) The string APPAPT contains two PAT's as substrings. The first one is formed ...