第 05 章 Spring Boot 整合持久层技术

前言

当前版本mysql8.0.15
父pom

 <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>jdbc</module>
        <module>mybatis</module>
        <module>jpa</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.11.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>sql</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sql</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <!--开启全局依赖管理-->
    <dependencyManagement></dependencyManagement>
    <dependencies>
        <!--连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!--对应数据库版本号-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

JDBC

SpringBoot整合jdbc

jdbc.pom

    <parent>
        <artifactId>sql</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>jdbc</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    </dependencies>

application.properties

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.password=123456
spring.datasource.username=root
spring.datasource.url=jdbc:mysql://localhost:3306/learn2020?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

DataSource装载

@Configuration
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class,
    DruidStatViewServletConfiguration.class,
    DruidWebStatFilterConfiguration.class,
    DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
    private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);
    @Bean(initMethod = "init")
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        LOGGER.info("Init DruidDataSource");
        return new DruidDataSourceWrapper();
    }
}

jdbcTemplate的装载

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {

}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(JdbcOperations.class)
class JdbcTemplateConfiguration {

	@Bean
	@Primary
	JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		JdbcProperties.Template template = properties.getTemplate();
		jdbcTemplate.setFetchSize(template.getFetchSize());
		jdbcTemplate.setMaxRows(template.getMaxRows());
		if (template.getQueryTimeout() != null) {
			jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());
		}
		return jdbcTemplate;
	}

}

自定义类

public class User {
    private Integer id;
    private String username;
    private String password;
}
@Service
public class UserService {

    JdbcTemplate jdbcTemplate;

    public UserService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public boolean addUser(User user) {
        int update = jdbcTemplate.update("insert into USER (username,password) values (?,?);",
                user.getUsername(), user.getPassword());
        if (update > 0) return true;
        return false;
    }

    public boolean updateUserById(User user) {
        int update = jdbcTemplate.update("update user set username = ?,password=? where id=? ;",
                user.getUsername(), user.getPassword(), user.getId());
        if (update > 0) return true;
        return false;
    }

    public boolean deleteUserById(User user) {
        int update = jdbcTemplate.update("delete from user where id=?;",
                user.getId());
        if (update > 0) return true;
        return false;
    }

    public List<User> selectUser() {
        List<User> result = (List<User>) jdbcTemplate.query("select * from USER", (rs, rowNum) -> {
            return new User(rs.getInt("id"),
                    rs.getString("username"),
                    rs.getString("password"));
        });
        if (result != null) return result;
        return null;
    }

    //需要数据库表和对象字段名称一一对应
    public List<User> selectUser2() {
        List<User> result = jdbcTemplate.query("select * from USER", new BeanPropertyRowMapper<>(User.class));
        if (result != null) return result;
        return null;
    }
	//自定义行映射
    public List<User> selectUserById(User user) {
        List<User> result = jdbcTemplate.query("select * from USER where id=?;", (rs, rowNum) -> {
            return new User(rs.getInt("id"),
                    rs.getString("username"),
                    rs.getString("password"));
        }, user.getId());
        if (result != null) return result;
        return null;
    }
}

测试

@SpringBootTest
public class JdbcTest {
    @Autowired
    UserService userService;
    @Test
    public void selectUser() {
        System.out.println(new ArrayList<>(userService.selectUser()).toString());
    }
}
2020-12-09 14:39:19.190  INFO 6800 --- [           main] com.example.JdbcTest                     : Started JdbcTest in 2.688 seconds (JVM running for 4.299)
[User{id=1, username='root1', password='1234561'}, User{id=3, username='root2', password='1234562'}, User{id=4, username='root2', password='1234562'}]
2020-12-09 14:39:20.515  INFO 6800 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

JDBC多数据源

在这里插入图片描述
在这里插入图片描述
配置多个数据源
application.properties

spring.datasource.first.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.first.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.first.password=123456
spring.datasource.first.username=root
spring.datasource.first.url=jdbc:mysql://localhost:3306/learn2020?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

spring.datasource.second.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.second.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.second.password=123456
spring.datasource.second.username=root
spring.datasource.second.url=jdbc:mysql://localhost:3306/learn2020_1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

DatabaseConfig

@Configuration
public class DatabaseConfig {

    //为bean属性赋值指定属性绑定前缀
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.first")
    DataSource first() {
        return DruidDataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.second")
    DataSource second() {
        return DruidDataSourceBuilder.create().build();
    }
}

JdbcTempleConfig

@Configuration
public class JdbcTemplateConfig {
    @Bean(name = "jdbcFirst")
    JdbcTemplate first(@Qualifier("first") DataSource first) {
        return new JdbcTemplate(first);
    }

    @Bean(name = "jdbcSecond")
    JdbcTemplate second(@Qualifier("second") DataSource second) {
        return new JdbcTemplate(second);
    }
}

UserServiceMulti

@Service
public class UserServiceMulti {
    JdbcTemplate first;
    JdbcTemplate second;

    public UserServiceMulti(@Qualifier("jdbcFirst") JdbcTemplate first,
                            @Qualifier("jdbcSecond") JdbcTemplate second) {
        this.first = first;
        this.second = second;
    }

    public List<User> selectFirst() {
        return first.query("select * from user", new BeanPropertyRowMapper<>(User.class));
    }
    public List<User> selectSecond() {
        return second.query("select * from user", new BeanPropertyRowMapper<>(User.class));
    }
}

Test

    @Test
    public void selectMulti() {
        System.out.println(new ArrayList<>(multi.selectFirst()).toString());
        System.out.println(new ArrayList<>(multi.selectSecond()).toString());
    }
2020-12-09 15:07:54.292  INFO 14484 --- [           main] com.example.JdbcTest                     : Started JdbcTest in 2.266 seconds (JVM running for 3.405)
2020-12-09 15:07:54.630  INFO 14484 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
[User{id=1, username='root1', password='1234561'}, User{id=3, username='root2', password='1234562'}, User{id=4, username='root2', password='1234562'}]
2020-12-09 15:07:55.631  INFO 14484 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited
[User{id=1, username='database2', password='1'}, User{id=2, username='database2', password='2'}]
2020-12-09 15:07:55.671  INFO 14484 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

Mybatis

SpringBoot整合Mybatis

mybatis.pom

    <parent>
        <artifactId>sql</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>mybatis</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>

application.properties

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.password=123456
spring.datasource.username=root
spring.datasource.url=jdbc:mysql://localhost:3306/learn2020?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

启动类

@SpringBootApplication
//定义mapper.xml扫描包
@MapperScan(basePackages = "com.example.service")
public class MybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisApplication.class,args);
    }
}

interface

package com.example.service;
public interface UserMapper {
    List<User> getAllUser();
}

bean

public class User {
    private Integer id;
    private String username;
    private String password;

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.service.UserMapper">
    <select id="getAllUser" resultType="com.example.bean.User">
        select * from user
    </select>
</mapper>

装配


  /**Register bean definitions as necessary based on the given annotation 
	*metadata of the importing @Configuration class.
	*Note that BeanDefinitionRegistryPostProcessor types may not be 							     
	*registered here, due to lifecycle constraints related to 	
	*@Configuration 		 
	*class processing.
	*The default implementation is empty.
   */
  org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
          generateBaseBeanName(importingClassMetadata, 0));
    }
  }



@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    applyConfiguration(factory);
    if (this.properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }
    if (!ObjectUtils.isEmpty(this.interceptors)) {
      factory.setPlugins(this.interceptors);
    }
    if (this.databaseIdProvider != null) {
      factory.setDatabaseIdProvider(this.databaseIdProvider);
    }
    if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
      factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
    }
    if (this.properties.getTypeAliasesSuperType() != null) {
      factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
    }
    if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }
    if (!ObjectUtils.isEmpty(this.typeHandlers)) {
      factory.setTypeHandlers(this.typeHandlers);
    }
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }
    Set<String> factoryPropertyNames = Stream
        .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
        .collect(Collectors.toSet());
    Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
    if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
      // Need to mybatis-spring 2.0.2+
      factory.setScriptingLanguageDrivers(this.languageDrivers);
      if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
        defaultLanguageDriver = this.languageDrivers[0].getClass();
      }
    }
    if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
      // Need to mybatis-spring 2.0.2+
      factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
    }

    return factory.getObject();
  }
    @Bean
  @ConditionalOnMissingBean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
      return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
      return new SqlSessionTemplate(sqlSessionFactory);
    }
  }
}

test

@SpringBootTest
public class example {
    @Autowired
    UserMapper userMapper;
    @Test
    public void selectOrigin(){
        System.out.println(userMapper.getAllUser());
    }
2020-12-09 16:09:57.601  INFO 8600 --- [           main] com.example.example                      : Started example in 15.602 seconds (JVM running for 17.097)
[User{id=1, username='root1', password='1234561'}, User{id=3, username='root2', password='1234562'}, User{id=4, username='root2', password='1234562'}]
2020-12-09 16:09:58.959  INFO 8600 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closing ...
2020-12-09 16:09:58.963  INFO 8600 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closed

Mybatis多数据源

application.properties

spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.one.password=123456
spring.datasource.one.username=root
spring.datasource.one.url=jdbc:mysql://localhost:3306/learn2020?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.two.password=123456
spring.datasource.two.username=root
spring.datasource.two.url=jdbc:mysql://localhost:3306/learn2020_1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

MybatisMultiConfig

@Configuration
@MapperScans(
        {
                @MapperScan(basePackages = "com.example.mapper1", sqlSessionFactoryRef = "sqlSessionFactory1",
                        sqlSessionTemplateRef = "sqlSessionTemplate1"),
                @MapperScan(basePackages = "com.example.mapper2", sqlSessionFactoryRef = "sqlSessionFactory2",
                        sqlSessionTemplateRef = "sqlSessionTemplate2")
        }
)
public class MybatisMultiConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.one")
    DataSource first() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.two")
    DataSource second() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    SqlSessionFactory sqlSessionFactory1(@Qualifier("first") DataSource first) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(first);
        return bean.getObject();
    }
    @Bean
    SqlSessionTemplate sqlSessionTemplate1(@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    SqlSessionFactory sqlSessionFactory2(@Qualifier("second") DataSource second) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(second);
        return bean.getObject();
    }

    @Bean
    SqlSessionTemplate sqlSessionTemplate2(@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

test

@SpringBootTest
public class example {
    @Autowired
    UserMapper1 userMapper1;
    @Autowired
    UserMapper2 userMapper2;
    @Test
    public void selectMulti(){
        System.out.println(userMapper1.getAllUser());
        System.out.println(userMapper2.getAllUser());
    }
}
2020-12-09 16:23:29.815  INFO 3096 --- [           main] com.example.example                      : Started example in 2.801 seconds (JVM running for 4.004)
2020-12-09 16:23:30.163  INFO 3096 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
[User{id=1, username='root1', password='1234561'}, User{id=3, username='root2', password='1234562'}, User{id=4, username='root2', password='1234562'}]
2020-12-09 16:23:31.374  INFO 3096 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited
[User{id=1, username='database2', password='1'}, User{id=2, username='database2', password='2'}]
2020-12-09 16:23:31.411  INFO 3096 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2020-12-09 16:23:31.412  INFO 3096 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} closing ...
2020-12-09 16:23:31.414  INFO 3096 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} closed
2020-12-09 16:23:31.415  INFO 3096 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closing ...
2020-12-09 16:23:31.415  INFO 3096 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closed

JPA

SpringBoot整合Jpa

jpa.pom

    <parent>
        <artifactId>sql</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>jpa</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
    </dependencies>

application.properties

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.password=123456
spring.datasource.username=root
spring.datasource.url=jdbc:mysql://localhost:3306/learn2020?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

#展示每次操作生成的sql语句
spring.jpa.show-sql=true
#spring.jpa.database=mysql
#spring.jpa.database-platform=mysql
#只有不存在或者修改后才创建修改表
spring.jpa.hibernate.ddl-auto=update
#确认默认引擎是innodb而不MyISAM
#hibernate.dialect.storage_engine=innodb

#AvailableSettings.DIALECT
#通过AvailableSettings设置hibernate的可用的配置
#如下配置是表明操作的数据库是mysql,数据库引擎为innodb而不是myisam
#org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

Entity

//标注为一个实体(是一个表)
@Entity
public class Book {
    //标注这个字段是主键ID
    @Id
    //配置主键生成策略
    //GenerationType.IDENTITY表示自增ID
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String author;

接口

public interface BookService extends JpaRepository<Book, Integer> {
    //下面所有为自定义查询方法
    Book findBookById(Integer id);

    List<Book> findBookByIdGreaterThan(Integer id);

    @Query(value = "select * from Book  where id=(select max(id) from Book )", nativeQuery = true)
    Book getMaxIdBook();


    //对于表的修改操作必须添加事务才能操作
    //?1按照方法顺序查询参数
    @Query(value = "insert into Book(name,author) values(?1,?2)", nativeQuery = true)
    @Modifying
    @Transactional
    Integer addBook(String name, String author);

    //:会自动查询@Param参数
    @Query(value = "insert into Book(name,author) values(:name,:author)", nativeQuery = true)
    @Modifying
    @Transactional
    Integer addBook1(@Param("name")String name,@Param("author") String author);
}

RepositoryBaseClass装载

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true",
		matchIfMissing = true)
@Import(JpaRepositoriesRegistrar.class)
@AutoConfigureAfter({ HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
public class JpaRepositoriesAutoConfiguration {
}

org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration
@EnableJpaRepositories
private static class EnableJpaRepositoriesConfiguration {
}

org.springframework.data.jpa.repository.config.EnableJpaRepositories#repositoryFactoryBeanClass
Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;

org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean#afterPropertiesSet
	@Override
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
	}
org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#afterPropertiesSet
	public void afterPropertiesSet() {

		this.factory = createRepositoryFactory();
		this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
		this.factory.setNamedQueries(namedQueries);
		this.factory.setEvaluationContextProvider(
				evaluationContextProvider.orElseGet(() -> QueryMethodEvaluationContextProvider.DEFAULT));
		this.factory.setBeanClassLoader(classLoader);
		this.factory.setBeanFactory(beanFactory);

		if (publisher != null) {
			this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
		}

		repositoryBaseClass.ifPresent(this.factory::setRepositoryBaseClass);

		RepositoryFragments customImplementationFragment = customImplementation //
				.map(RepositoryFragments::just) //
				.orElseGet(RepositoryFragments::empty);

		RepositoryFragments repositoryFragmentsToUse = this.repositoryFragments //
				.orElseGet(RepositoryFragments::empty) //
				.append(customImplementationFragment);

		this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);

		// Make sure the aggregate root type is present in the MappingContext (e.g. for auditing)
		this.mappingContext.ifPresent(it -> it.getPersistentEntity(repositoryMetadata.getDomainType()));

		this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));

		if (!lazyInit) {
			this.repository.get();
		}
	}
	
org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryInformation(org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.repository.core.support.RepositoryComposition)
	private RepositoryInformation getRepositoryInformation(RepositoryMetadata metadata,
			RepositoryComposition composition) {

		RepositoryInformationCacheKey cacheKey = new RepositoryInformationCacheKey(metadata, composition);

		return repositoryInformationCache.computeIfAbsent(cacheKey, key -> {

			Class<?> baseClass = repositoryBaseClass.orElse(getRepositoryBaseClass(metadata));

			return new DefaultRepositoryInformation(metadata, baseClass, composition);
		});
	}
org.springframework.data.jpa.repository.support.JpaRepositoryFactory#getRepositoryBaseClass
	@Override
	protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
		return SimpleJpaRepository.class;
	}


org.springframework.data.jpa.repository.support.JpaRepositoryFactory#getRepositoryBaseClass
	@Override
	protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
		return SimpleJpaRepository.class;
	}


配置属性设值

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionImplementor.class })
@EnableConfigurationProperties(JpaProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class })
@Import(HibernateJpaConfiguration.class)
public class HibernateJpaAutoConfiguration {

}

org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration#getVendorProperties
	@Override
	protected Map<String, Object> getVendorProperties() {
		Supplier<String> defaultDdlMode = () -> this.defaultDdlAutoProvider.getDefaultDdlAuto(getDataSource());
		return new LinkedHashMap<>(this.hibernateProperties
				.determineHibernateProperties(getProperties().getProperties(), new HibernateSettings()
						.ddlAuto(defaultDdlMode).hibernatePropertiesCustomizers(this.hibernatePropertiesCustomizers)));
	}

JpaRepository本身自带一些查询方法
在这里插入图片描述
具体操作通过jdk代理实现
在这里插入图片描述
test

@SpringBootTest
public class BookTest {
    @Autowired
    BookService1 bookService;
    @Test
    public void add(){
        bookService.save(new Book("水调歌头","苏轼"));
    }
    @Test
    public void select(){
        System.out.println(bookService.findAll());
    }
    @Test
    public void selectDesc(){
        List<Book> books = bookService.findAll(Sort.by(Sort.Direction.DESC, "id"));
        System.out.println(books);
    }
    @Test
    public void selectPageable(){
        Page<Book> books = bookService.findAll(PageRequest.of(1,2));
        System.out.println("当前页内容"+books.getContent());
        System.out.println("是否为首页"+books.isFirst());
        System.out.println("是否为尾页"+books.isLast());
        System.out.println("总页数"+books.getTotalPages());
        System.out.println("当前页元素个数"+books.getNumberOfElements());
        System.out.println("当前页下标数"+books.getNumber());
    }
    @Test
    public void selectById(){
        System.out.println(bookService.findBookById(1));
        System.out.println(bookService.findBookByIdGreaterThan(2));
        System.out.println(bookService.getMaxIdBook());
    }

Jpa多数据源

application.properties

spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.one.password=123456
spring.datasource.one.username=root
spring.datasource.one.url=jdbc:mysql://localhost:3306/learn2020?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.two.password=123456
spring.datasource.two.username=root
spring.datasource.two.url=jdbc:mysql://localhost:3306/learn2020_1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
#spring.jpa.properties.hibernate.ddl-auto无法生效
#spring.jpa.properties.hibernate.ddl-auto=update
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

JpaMultiConfig

@Configuration
public class JpaMultiConfig {
    @Autowired
    JpaProperties properties;
    @Autowired
    HibernateProperties hibernateProperties;
    @Autowired
    HibernateSettings settings;

    @Bean
    HibernateSettings settings() {
        return new HibernateSettings();
    }

    class DatasourceConfig {
        @Bean
        @ConfigurationProperties("spring.datasource.one")
        @Primary
        DataSource first() {
            return DruidDataSourceBuilder.create().build();
        }

        @Bean
        @ConfigurationProperties("spring.datasource.two")
        DataSource second() {
            return DruidDataSourceBuilder.create().build();
        }
    }

    @EnableJpaRepositories(basePackages = "com.example.service1",
            entityManagerFactoryRef = "factoryBean1",
            transactionManagerRef = "transactionManager1")
    class JpaConfig1 {
        @Bean
        @Primary
        LocalContainerEntityManagerFactoryBean factoryBean1(EntityManagerFactoryBuilder builder,
                                                            @Qualifier("first") DataSource dataSource) {
            return builder.dataSource(dataSource)
                    .properties(
                            hibernateProperties.determineHibernateProperties(
                                    properties.getProperties(), settings
                            )
                    )
                    .packages("com.example.bean")
                    .persistenceUnit("jpa1")
                    .build();
        }

        @Bean
        @Primary
        PlatformTransactionManager transactionManager1(EntityManagerFactoryBuilder builder,
                                                       @Qualifier("factoryBean1") LocalContainerEntityManagerFactoryBean bean) {
            return new JpaTransactionManager(bean.getObject());
        }
    }

    @EnableJpaRepositories(basePackages = "com.example.service2",
            entityManagerFactoryRef = "factoryBean2",
            transactionManagerRef = "transactionManager2")
    class JpaConfig2 {
        @Bean
        LocalContainerEntityManagerFactoryBean factoryBean2(EntityManagerFactoryBuilder builder,
                                                            @Qualifier("second") DataSource dataSource) {
            System.out.println(new HashSet(properties.getProperties().values()).toString());
            return builder.dataSource(dataSource)
                    .properties(
                            hibernateProperties.determineHibernateProperties(
                                    properties.getProperties(), settings
                            )
                    )
                    .packages("com.example.bean")
                    .persistenceUnit("jpa2")
                    .build();
        }

        @Bean
        PlatformTransactionManager transactionManager2(EntityManagerFactoryBuilder builder,
                                                       @Qualifier("factoryBean2") LocalContainerEntityManagerFactoryBean bean) {
            return new JpaTransactionManager(bean.getObject());
        }
    }
}

test

    @Autowired
    BookService1 service1;
    @Autowired
    BookService2 service2;
    @Test
    public void selectMulti(){
        System.out.println(service1.findAll());
        System.out.println(service2.findAll());
    }
Hibernate: 
    select
        book0_.id as id1_0_,
        book0_.author as author2_0_,
        book0_.name as name3_0_ 
    from
        book book0_
[Book{id=1, name='三国', author='罗贯中'}, Book{id=2, name='西游记', author='吴承恩'}, Book{id=3, name='红楼梦', author='曹雪芹'}, Book{id=4, name='观沧海', author='曹操'}, Book{id=5, name='水调歌头', author='苏轼'}, Book{id=6, name='test?', author='test?'}, Book{id=7, name='test:', author='test:'}, Book{id=9, name='test?1', author='test?1'}, Book{id=10, name='test:1', author='test:1'}]
Hibernate: 
    select
        book0_.id as id1_0_,
        book0_.author as author2_0_,
        book0_.name as name3_0_ 
    from
        book book0_
[]

总结

JPA数据查询方法声明规范
在这里插入图片描述
自定义查询通过@Query注解
例如

    @Query(value = "select * from Book  where id=(select max(id) from Book )", nativeQuery = true)
    Book getMaxIdBook();

原生的sql需要对nativeQuery 属性赋值为true,否则默认是jpa自带的一种查询语言规范

对于表的修改操作(修改,删除,添加)必须添加事务才能操作,同时需要标注为 @Modifying,表明这是一个修改语句

    @Query(value = "insert into Book(name,author) values(?1,?2)", nativeQuery = true)
    @Modifying
    @Transactional

否则会报错

Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query

默认情况下, Spring Data 的每个方法上有事务, 但都是一个只读事务. 他们不能完成修改操作
jpa事务的注意事项:

Spring Data 提供了默认的事务处理方式,即所有的查询均声明为只读事务。
对于自定义的方法,如需改变 Spring Data 提供的事务默认方式,可以在方法上添加 @Transactional 注解。
进行多个 Repository 操作时,也应该使它们在同一个事务中处理,按照分层架构的思想,这部分属于业务逻辑层,因此,需要在Service 层实现对多个 Repository 的调用,并在相应的方法上声明事务。
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值