SpringBoot整合JPA以及多数据源

Lou.Chen
大约 6 分钟

springboot整合Jpa

1、基本配置:

①pom.xml配置:
<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.18</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
			<version>5.1.27</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</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>

②yaml配置:
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456

  jpa:
#    显示sql语句
    show-sql: true
#    数据库
    database: mysql
    #    数据库平台
    database-platform: mysql
#    设置表的结构的生成类型
    hibernate:
#      每次启动更新表结构
      ddl-auto: update
    properties:
      hibernate:
#        设置方言 mysql57
        dialect: org.hibernate.dialect.MySQL57Dialect

③实体配置:
/**
 * 指定表的名称 若不存在 则自动为你创建此表
 */
@Entity(name = "t_book")
@Getter
@Setter
@ToString
public class Book {

    /**
     *在jpa中一个表必须设置主键,否则创建会失败
     * @Id标注的属性为主键
     * @GeneratedValue指定生成策略为自增长
     * @column中可设置字段的约束操作(长度为10)
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(length = 10)
    private Integer id;

    /**
     * 设置自动不能为空,长度为50,设置数据库中和属性的对应名称
     */
    @Column(nullable = false,length = 50,name = "name")
    private String name;

    /**
     * 设置自动不能为空,长度为20,设置数据库中和属性的对应名称
     */
    @Column(nullable = false,length = 20,name = "author")
    private String author;
}

④jpa接口配置:
/**
 * 这里要实现 JpaRepository 接口
 * JpaRepository已经帮我们实现了很多操作直接使用即可
 * 第一个参数为 操作的实体的类型
 * 第二个参数为 主键的类型
 */
public interface BookDao extends JpaRepository<Book,Integer> {

}
⑤测试:
@SpringBootTest
class JpaApplicationTests {

	@Autowired
	private BookDao bookDao;

	/**
	 * 增加
	 */
	@Test
	void addBokk() {
		Book book=new Book();
		book.setName("西游记");
		book.setAuthor("施耐庵");
		bookDao.save(book);
	}

	/**
	 * 有则更新 无则添加
	 */
	@Test
	void updateBook() {
		Book book=new Book();
		book.setName("xiyouji");
		book.setAuthor("shinaian");
		book.setId(1);
		bookDao.saveAndFlush(book);
	}

	/**
	 * 通过id删除
	 */
	@Test
	void deleteBook() {
		bookDao.deleteById(1);
	}

	/**
	 * 通过id查询  查询所有
	 */
	@Test
	void findBook() {
		Optional<Book> byId = bookDao.findById(2);
		System.out.println(byId.get());
		List<Book> all = bookDao.findAll();
		System.out.println(all);
	}

	/**
	 * 按照id 降序排序
	 */
	@Test
	void findBookSort() {
		List<Book> all = bookDao.findAll(Sort.by(Sort.Direction.DESC, "id"));
		System.out.println(all);
	}

	/**
	 * 分页查询
	 */
	@Test
	void findBookPage() {
		//从第一页开始查询(起始为0),每页大小为2
		Pageable pageable = PageRequest.of(2, 2);
		Page<Book> all = bookDao.findAll(pageable);
		System.out.println("总记录数:"+all.getTotalElements());
		System.out.println("当前页记录数:"+all.getNumberOfElements());
		System.out.println("每页记录数:" + all.getSize());
		System.out.println("总页数:" + all.getTotalPages());
		System.out.println("查询结果数据:" + all.getContent());
		System.out.println("当前页数(从0开始):" + all.getNumber());
		System.out.println("是否为首页:" + all.isFirst());
		System.out.println("是否为尾页:" + all.isLast());
	}
}

2、jpa使用关键字进行查询:

关键字查询参考表:
KeywordSampleJPQL snippet
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is,EqualsfindByFirstname,findByFirstnameIs… 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 ages)… where x.age in ?1
NotInfindByAgeNotIn(Collection ages)… where x.age not in ?1
TruefindByActiveTrue()… where x.active = true
FalsefindByActiveFalse()… where x.active = false
IgnoreCasefindByFirstnameIgnoreCase… where UPPER(x.firstame) = UPPER(?1)
①接口配置

/**
 * 这里要实现 JpaRepository 接口
 * JpaRepository已经帮我们实现了很多操作直接使用即可
 * 第一个参数为 操作的实体的类型
 * 第二个参数为 主键的类型
 */
public interface BookDao extends JpaRepository<Book,Integer> {

    /**
     * 根据id查找
     * @param id
     * @return
     */
    Book findBookById(Integer id);

    /**
     * 查询比id小的
     * @param integer
     * @return
     */
    List<Book> findBookByIdLessThan(Integer integer);

    /**
     * 查询比id大的或者包含此名称的
     * @param id
     * @param name
     * @return
     */
    List<Book> findBookByIdGreaterThanOrNameContaining(Integer id, String name);

}

②测试
    @Test
	void customeFind(){
		Book bookById = bookDao.findBookById(4);
		System.out.println(bookById);
		List<Book> bookByIdLessThan = bookDao.findBookByIdLessThan(3);
		System.out.println(bookByIdLessThan);
		List<Book> finc = bookDao.findBookByIdGreaterThanOrNameContaining(6, "马");
		System.out.println(finc);
	}

3、jpa自定义sql查询

@Query

标记为自定义的sql查询,nativeQuery=true,使用原生的sql操作

@Modifying

标记为更新删除或新增的方法

@Transactional

当方法为更新删除或者新增的时候,需要在自定义的方法上加上事务,或者在方法所在的dao或者service上加上事务@Transactional

两种传参形式:

① 冒号形式

Query(value = "insert into t_book(name,author) values(:name,:author)",nativeQuery = true)
Integer addBook(@Param("name")String name,@Param("author")String author);

② ?形式

@Modifying
@Transactional 
@Query(value = "insert into t_book(name,author) values(?1,?2)",nativeQuery = true)
 Integer addBook1(String name,String author);
/**
 * 这里要实现 JpaRepository 接口
 * JpaRepository已经帮我们实现了很多操作直接使用即可
 * 第一个参数为 操作的实体的类型
 * 第二个参数为 主键的类型
 */
public interface BookDao extends JpaRepository<Book,Integer> {

    @Query(value = "select * from t_book where id=(select max(id) from t_book)",nativeQuery = true)
    Book getMaxIdBook();
    
    @Modifying
    @Transactional
    @Query(value = "insert into t_book(name,author) values(:name,:author)",nativeQuery = true)
    Integer addBook(@Param("name")String name,@Param("author")String author);

    @Modifying
    @Transactional
    @Query(value = "insert into t_book(name,author) values(?1,?2)",nativeQuery = true)
    Integer addBook1(String name,String author);

}

4、多数据源配置

①pom.xml配置一致
②yaml配置
spring:
  datasourceone:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456

  datasourcetwo:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://47.96.141.44:3306/test?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 147258369

  jpa:
    #    显示sql语句
    show-sql: true
    #    数据库
    database: mysql
    #    数据库平台
    database-platform: mysql
    #    设置表的结构的生成类型
    hibernate:
      #      每次启动更新表结构
      ddl-auto: create
    properties:
      hibernate:
#        搜索mysql57即可 找到类路径
        #        设置方言 mysql57
        dialect: org.hibernate.dialect.MySQL57Dialect
③实体bean
/**
 * 指定表的名称 若不存在 则自动为你创建此表
 */
@Entity(name = "t_book")
@Getter
@Setter
@ToString
public class Book {

    /**
     *在jpa中一个表必须设置主键,否则创建会失败
     * @Id标注的属性为主键
     * @GeneratedValue指定生成策略为自增长
     * @column中可设置字段的约束操作(长度为10)
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(length = 10)
    private Integer id;

    /**
     * 设置自动不能为空,长度为50,设置数据库中和属性的对应名称
     */
    @Column(nullable = false,length = 50,name = "name")
    private String name;

    /**
     * 设置自动不能为空,长度为20,设置数据库中和属性的对应名称
     */
    @Column(nullable = false,length = 20,name = "author")
    private String author;
}

④数据源注入
@Configuration
public class DataSourceConfig {

    @Primary
    @Bean
    @ConfigurationProperties("spring.datasourceone")
    DataSource dataSourceOne(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasourcetwo")
    DataSource dataSourceTwo() {
        return DruidDataSourceBuilder.create().build();
    }

}

⑤事务及LocalContainerEntityManagerFactoryBean配置
/**
 * basePackages 扫描所在接口的包
 * entityManagerFactoryRef 引用配置的localContainerEntityManagerFactoryBeanOne
 * transactionManagerRef  引用配置的 事务platformTransactionManagerOne
 */
@EnableJpaRepositories(basePackages = "org.lc.jpa.dao.dao1",entityManagerFactoryRef = "localContainerEntityManagerFactoryBeanOne",transactionManagerRef = "platformTransactionManagerOne")
@Configuration
public class JpaConfigOne {

    @Autowired
    @Qualifier("dataSourceOne")
    DataSource dataSourceOne;

    //注入yaml中的其他属性配置
    @Autowired
    JpaProperties jpaProperties;

    @Bean
    LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBeanOne(EntityManagerFactoryBuilder builder) {
        //注入数据源
        return builder.dataSource(dataSourceOne)
//                配置操作的实体bean
                .packages("org.lc.jpa.bean")
//                注入配置属性文件
                .properties(jpaProperties.getProperties())
//                初始化单元 不同即可
                .persistenceUnit("pu1")
                .build();
    }

    /**
     * 事务的配置
     * @param builder
     * @return
     */
    @Bean
    PlatformTransactionManager platformTransactionManagerOne(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(localContainerEntityManagerFactoryBeanOne(builder).getObject());
    }

}
/**
 * basePackages 扫描所在接口的包
 * entityManagerFactoryRef 引用配置的localContainerEntityManagerFactoryBeanOne
 * transactionManagerRef  引用配置的 事务platformTransactionManagerOne
 */
@EnableJpaRepositories(basePackages = "org.lc.jpa.dao.dao2",entityManagerFactoryRef = "localContainerEntityManagerFactoryBeanTwo",transactionManagerRef = "platformTransactionManagerTwo")
@Configuration
public class JpaConfigTwo {

    @Autowired
    @Qualifier("dataSourceTwo")
    DataSource dataSourceTwo;

    //注入yaml中的其他属性配置
    @Autowired
    JpaProperties jpaProperties;

    @Primary
    @Bean
    LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBeanTwo(EntityManagerFactoryBuilder builder) {
        //注入数据源
        return builder.dataSource(dataSourceTwo)
//                配置操作的实体bean
                .packages("org.lc.jpa.bean")
//                注入配置属性文件
                .properties(jpaProperties.getProperties())
//                初始化单元 不同即可
                .persistenceUnit("pu1")
                .build();
    }

    /**
     * 事务的配置
     * @param builder
     * @return
     */
    @Primary
    @Bean
    PlatformTransactionManager platformTransactionManagerTwo(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(localContainerEntityManagerFactoryBeanTwo(builder).getObject());
    }
}
⑥两个不同包及名称的接口
public interface BookDaoOne extends JpaRepository<Book,Integer> {
    
}
public interface BookDaoTwo extends JpaRepository<Book,Integer> {
    
}
⑦测试

注入不同的dao进行测试

	@Autowired
	private BookDaoOne bookDaoOne;	
	@Autowired
	private BookDaoTwo bookDaoTwo;

	@Test
	void multipleFind() {
		List<Book> all = bookDaoOne.findAll();
		System.out.println(all);
		List<Book> all1 = bookDaoTwo.findAll();
		System.out.println(all1);
	}