springboot(MyBatis多数据源配置与JPA)

基于Mybatis配置多数据源

导入mysql驱动,mybatis,lombok

  1. 在 application.properties 中配置数据源信息

    #数据源1
    spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.one.jdbc-url=jdbc:mysql://localhost:3306/boot1?characterEncoding=utf8&serverTimezone=GMT%2B8
    spring.datasource.one.username=root
    spring.datasource.one.password=034312
    
    #需要注意的是,springboot2.0以上配置双数据源,配置文件中不能写url,而是要改成jdbc-url,否则会出错。
    
    #数据源2
    spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.two.jdbc-url=jdbc:mysql://localhost:3306/boot2?characterEncoding=utf8&serverTimezone=GMT%2B8
    spring.datasource.two.username=root
    spring.datasource.two.password=034312
    
  2. 创建DataSourceConfig配置类

    @Configuration
    public class DataSourceConfig {
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.one")
        DataSource dsOne() {
            return DataSourceBuilder.create().build();
        }
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.two")
        DataSource dsTwo() {
            return DataSourceBuilder.create().build();
        }
    }
    
  3. 创建MybatisConfig***配置类

    以MybatisConfiOne为例

    @Configuration
    @MapperScan(basePackages = "com.zhj.mapper1",sqlSessionTemplateRef = "sqlSessionTemplate1")
    public class MyBatisConfigOne {
        @Resource(name = "dsOne")
        DataSource dsOne;
        @Bean
        SqlSessionFactory sqlSessionFactory1() {
            SqlSessionFactory sessionFactory = null;
            try {
                SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
                bean.setDataSource(dsOne);
                bean.setTypeAliasesPackage("com.zhj.domain");
                sessionFactory = bean.getObject();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return sessionFactory;
        }
        @Bean
        SqlSessionTemplate sqlSessionTemplate1() {
            return new SqlSessionTemplate(sqlSessionFactory1());
        }
    }
    
  4. 然后 后面正常创建 对应的 mapper ,service,controller

  5. 注意: pom文件配置

    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
    </resources> 
    

SpringBoot中如何集成spring data jpa

一 什么是ORM

​ ORM即Object-Relational Mapping,他的作用是关系型数据库和对象之间的映射。这样,我们在具体操作数据库的时候,就不需要和复杂的SQL语句打交道,只要像平常操作对象操作他就可以了

​ 在ORM出现之前,我们使用jdbc来操作数据库,但jdbc没有封装,对于大项目来说,很难实现MVC的概念,所以人们就开发了 ORM框架来解决这些问题。比如Hibernate,Mybatis,不过 Hibernate是完全的ORM框架,mybatis是半ORM框架,因为它需要手动建表和自己写sql

​ ORM的优点:提高了开发效率。由于ORM可以自动对Entity对象与数据库中的table进行字段与属性的映射,能够像操作对象一样 从数据库中获取数据

​ ORM的缺点:ORM的缺点是牺牲程序的执行效率,因为是自动生成的sql,所以实现复杂查询比较麻烦

二 什么是JPA

​ ORM框架很多,大家各自搞自己的,为了统一规范,就出了JPA

​ JPA全称Java Persistence API ,可以通过注解 或者 XML描述【对象-关系表】之间的映射关系,并将对象持久化到数据库中

​ JPA为我们提供了:

​ (1)ORM映射元数据:JPA 支持XML 和 注解两种元数据形式,元数据描述对象与对象之间的映射关系,框架据此将实体对象持久化到数据库表中

​ 如:@Entity,@Table,@Column,@Transient等注解

​ (2)API:用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来

​ 如:entityMananger.merge(T t)

​ (3)JPQL查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合

​ 但是:JPA仅仅是一种规范,也就是说JPA仅仅定义了一些接口,而接口需要实现才能工作,所以底层需要某种实现

​ 而Hibernate就是实现了JPA接口的ORM框架

三 什么是Spring Data Jpa?

​ 实现jpa中的接口需要写大量的代码,包括简单的增删改查,那可不可以有框架将这些写好呢,于是Spring Data Jpa 就出现了

​ Spring Data Jpa是spring提供一套简化JPA开发的框架,不仅有接口,也有实现类,只要按照约定好的【命名规则】写dao接口,就可以在不写接口实现的情况下,实现对数据库的访问和操作,同时提供了很多处理CRUD之外的功能,如分页 排序 复杂查询;

​ Spring Data JPA可以理解为JPA规范的再次封装抽象,底层还是使用了Hibernate的JPA技术实现

四 Spring Data JPA 和 Mybatis对比

​ 关于在开发中到底应该是使用JPA 还是使用 Mybatis 争论不休,总体来说,国外用JPA的多,国内用MyBatis的多

Spiring Data JPA是面向对象的思想,一个对象就是一个类,强化的是您对这个表的控制。spring data jpa实现了jpa 功能,即 可以实现pojo 转化为关系型数据库记录的功能,通俗来讲 可以不写任何建表sql 语句了。jpa 是spring data jpa功能的一个子集

MyBatis则是面向sql,您的结果完全来源于sql,而对象这个东西只是用来接受sql带来的结果集。您的一切操作都是围绕sql,包括动态根据条件约定sql语句等。mybatis并不是注意对象的概念。只要能接受数据就好

各自优缺点

​ 面向sql就更利于优化,因为sql可以优化的点太多的了。对于并发用户多,主求性能的,mybatis 更有优势。

​ 面向对象就更利于移植,可维护性,因为数据对象不依赖数据源。比如mysql换成oracle,jpa更方便。

​ 用哪个 最终取决于老板

五 Springboot 中如何集成spring data jpa

​ Spring boot 中使用JPA 实际上是Spring Data JPA ,在 Spring Data 中,只要您的方法名符合规范,他就知道您想干什么,不许要自己再去写SQL。

  1. 创建工程

    创建工程,添加Web,Jpa,MySql驱动,lombok依赖

  2. 添加druid 连接池依赖

    springboot默认的连接是hikari

    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>druid</artifactId>
             <version>1.2.6</version>
         </dependency>
    
  3. 配置连接信息 和 jpa 信息(application.properties)

    # 配置数据库基本信息
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/boot3?useUnicode=true&characterEncoding=utf8
    spring.datasource.username=root
    spring.datasource.password=034312
    # 更换数据源类型
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    
    # 配置JPA
    spring.jpa.database=mysql
    
    # 是否在控制台打印SQL
    spring.jpa.show-sql=true
    
    # 每次启动项目,数据库初始化策略
    #spring.jpa.hibernate.ddl-auto=create # 每次运行该程序没有表格会新建表,表内数据会清空
    #spring.jpa.hibernate.ddl-auto=create-drop # 每次程序结束是会会清空表
    #spring.jpa.hibernate.ddl-auto=update # 每次运行程序,没有表格会新建,表内有数据不会清空,只会更新
    #spring.jpa.hibernate.ddl-auto=validate # 运行程序会校验数据库 与 对象 中字段类型是否相同,不同会报错
    
    spring.jpa.hibernate.ddl-auto=update
    
    # 指定默认存储引擎为InnoDB,默认情况下自动创建表时会使用 MyISAM 作为表引擎
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
    
  4. 创建domain

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Entity(name = "user")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
        @Column(name = "name")
        private String name;
        @Column(name = "pwd")
        private String pwd;
    }
    
  5. 创建mapper

    public interface UserMapper extends JpaRepository<User,Integer> {
    }
    
  6. 在service 直接进行属性注入即可

六 Spring Data Api 核心接口

  1. Repository接口

    提供方法名称命名查询方式

    提供了基于@Query注解查询与 更新

  2. CrudRepository接口

    CrudRepository接口继承了Repository接口

    CrudRepository提供了基本的增删改查,不需要我们自定义

  3. PagingAndSortingRepository接口

    该接口继承了CrudRepository接口

    该该接口提供了分页与排序操作,也就是该接口不用自己定义增删改查方法和分页排序方法

  4. JpaRepository接口

    该接口继承了PagingAndSortingRepository

    对继承的父接口中的方法的返回值进行适配,也就是该接口不用自己定义增删改查和分页排序方法,并且让分页查询更加简单

  5. JpaSpecificationExcutor接口

    该接口主要是提供了多条件查询的支持,并且可以在查询中添加排序和分页。注意JPASpecificationExcutor是单独存在的。不继承上述接口

七 JpaRepository接口的基本应用

​ @Autowired

​ private Mapper mapper;

查询所有:mapper.findAll()

查询单个:mapper.findById(id),get()

添加: mapper.save(domain)

修改用户: mapper.getOne(id) mapper.save(domain)

删除用户:mapper.delete()

分页查询

public List<User> selectUserByPage(){
    PageRequest pageRequest = PageRequest.of(0, 3);
    Page<User> page = userMapper.findAll(pageRequest);
    List<User> userList = page.getContent();
    return userList;
}

降序查询

public List<User> selectUserBySort(){
    PageRequest pageRequest = PageRequest.of(0, 3, Sort.Direction.DESC,"id");
    Page<User> page = userMapper.findAll(pageRequest);
    List<User> userList = page.getContent();
    return userList;
}

八 spring data jpa 自定义查询

关键字示例JPQL 表达式
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
IsNull, NullfindByAge(Is)Null… 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)

例如:

public interface UserMapper extends JpaRepository<User,Integer> {
    List<User> findByIdLessThan(Integer id);
    List<User> findByNameLike(String keyword);
}

九 自定义sql

public interface UserMapper extends JpaRepository<User,Integer> {
    List<User> findByIdLessThan(Integer id);
    List<User> findByNameLike(String keyword);
    // 查询 id 最大的 user
    @Query(value = "select * from user where id = (select max(id) from user)",nativeQuery = true)
    User findUserByMaxId();

    // 自定义模糊查讯
    @Query(value = "select * from user where name like %:name%",nativeQuery = true)
    List<User> findUserNameByLike(@Param("name")String name);
}

十 复杂查询

  1. 一对一

    在实体类 配置 关联字段,其他不变

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Entity(name = "student")
    public class Student {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer sid;
        @Column(name = "name")
        private String name;
        @Column(name = "age")
        private Integer age;
        @OneToOne
      	// name 是本表字段  referencedColumnName 外表关联字段
        @JoinColumn(name = "gid",referencedColumnName = "gid")
        private Glass glass;
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Entity(name = "glass")
    public class Glass {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer gid;
        @Column(name = "gname")
        private String gname;
    }
    
  2. 一对多

    在 多端进行维护 如 一个班级有多个学生

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Entity(name = "glass")
    public class Glass {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer gid;
        @Column(name = "gname")
        private String glassName;
    
        @OneToMany(mappedBy = "glass") // 设置 一对多 mapperBy 把自己交由 他表 进行维护
        private List<Student> studentList;
    
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @EqualsAndHashCode(exclude = "glass") // jpa 使用lombok是需要 排除关联表属性
    @Entity(name = "student")
    public class Student {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer sid;
        @Column(name = "name")
        private String name;
        @Column(name = "age")
        private Integer age;
    
        @ManyToOne // 设置 多对一
        @JoinColumn(name = "gid")  // 外键 关联字段
        @JsonBackReference         // 避免 json回调关联
        private Glass glass;
    }
    
  3. 多对多

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @EqualsAndHashCode(exclude = "glass") // jpa 使用lombok是需要 排除关联表属性
    @Entity(name = "student")
    public class Student {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer sid;
        @Column(name = "name")
        private String name;
        @Column(name = "age")
        private Integer age;
    
        @ManyToMany
        // name : 中间表   joinColumns: 主表字段   inverseJoinColumns : 副表字段
        @JoinTable(name = "student_teacher",joinColumns = @JoinColumn(name = "sid"),inverseJoinColumns =  @JoinColumn(name ="tid"))
        private List<Teacher> teacherList;
    }
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity(name = "teacher")
    public class Teacher {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer tid;
        @Column(name = "tname")
        private String tname;
        @Column(name = "tcourse")
        private String tcourse;
    
    }
    

一对一 和 多对多 只需要设置 一张表 ,而一对多 是双向关联的关系 需要设置 两张表(多端为 维护端)

十一 JpaSpecificationExcutor 的使用

​ 作用 : 封装 where 以后的查询方法 例如:

public interface StudentMapper extends JpaRepository<Student,Integer>, JpaSpecificationExecutor<Student> {

}
@RequestMapping("select")
public List<Student> select(){
    Specification specification = new Specification<Student>() {
        @Override
        public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
            ArrayList<Predicate> predicateArrayList = new ArrayList<>();
            predicateArrayList.add(criteriaBuilder.equal(root.get("name"),"周弘杰"));
            predicateArrayList.add(criteriaBuilder.equal(root.get("age"),14));

            Predicate[] predicates = new Predicate[predicateArrayList.size()];

            return criteriaBuilder.and(predicateArrayList.toArray(predicates)); //  使用 and 查询 并返回
        }
    };
    return studentMapper.findAll(specification);
};

十二 JPQL

​ JPQL 全称java Persistence Query Language. 中文意思是 java 持久化查询语言

是一种可移植性查询语言,旨在以面向对象的表达式,将SQL语法和简单查询语义绑定在一起,使用这种语法编写的查询是 可移植性的,可以被编译所有主流数据库库服务器上的SQL

​ 其特征 与 原声的SQL 语句类似,并且完全面向对象,通过类名与属性访问,而不是 表名和表的属性

查询用的SELECT 语法如下:

比如 : select u from user u where u.userName :userName

User : 实体类名称 默认是 domain中的实体类名,如果使用了 注解@Entity(name=“”),则为name

u:别名

u.userName:实体对象的属性

:userName:是传递的参数

JPQL 语言不支持 增加操作,即 只有 delete,update ,select

public interface StudentMapper1 extends JpaRepository<Student,Integer>, JpaSpecificationExecutor<Student> {
    @Query("select s from student s where s.name=:name")
    List<Student> selectStudentByName(@Param("name") String name);
}

修改与删除

@Modifying // 默认为查询 设置为修改
@Transactional // 开启事务
@Query("update student s set s.name=:name where s.sid =:sid")
Integer updateStudentById(@Param("name") String name,@Param("sid") Integer sid);


@Modifying
@Transactional
@Query("delete from student s where s.sid=:sid ")
Integer deleteStudentById(@Param("sid") Integer sid);

十三 基于jpa 配置多数据源

  1. 创建工程,添加Web,Jpa,MySql驱动,lombok依赖 ,druid

  2. 在 applicaiton.properties 中配置 数据源 与 jpa 相关配置

    # 数据库的基本配置
    spring.datasource.one.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.one.username=root
    spring.datasource.one.password=034312
    #注意多数据源要用jdbc-url
    spring.datasource.one.jdbc-url=jdbc:mysql://localhost:3306/boot1? characterEncoding=utf8&serverTimezone=GMT%2B8   
    spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
    
    spring.datasource.two.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.two.username=root
    spring.datasource.two.password=034312
    spring.datasource.two.jdbc-url=jdbc:mysql://localhost:3306/boot2?characterEncoding=utf8&serverTimezone=GMT%2B8
    spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
    
    # JPA配置
    spring.jpa.properties.database=mysql
    spring.jpa.properties.show-sql=true
    spring.jpa.properties.hibernate.ddl-auto=update
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
    
  3. 配置DataSourceConfig:创建config 包,在包下 创建 DataSourceConfig

    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    
    import javax.sql.DataSource;
    
    @Configuration
    public class DataSourceConfig {
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.one")
        //这里添加@Primary注解,一定不能少,否则在项目启动时会出错,@Primary 表示当某一个类存在多个实例时,优先使用哪个实例
        @Primary
        DataSource dsOne() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.two")
        DataSource dsTwo() {
            return DataSourceBuilder.create().build();
        }
    }
    
  4. 在 config 下创建 JpaConfigOne 与 JpaConfigTwo

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
    import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.annotation.Resource;
    import javax.sql.DataSource;
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages = "com.zhj.mapper1",
            entityManagerFactoryRef = "entityManagerFactoryBeanOne",
            transactionManagerRef = "platformTransactionManagerOne")
    /*
    basePackages 用来指定 dao 所在的位置。
    entityManagerFactoryRef 用来指定实体类管理工厂 Bean 的名称
    transactionManagerRef 用来指定事务管理器的引用名称,
    默认的 Bean 名称为方法名
     */
    public class JpaConfigOne {
    
        @Resource(name = "dsOne")
        DataSource dsOne;
    
        @Autowired
        JpaProperties jpaProperties;
    
        @Bean
        @Primary
            //该 Bean 用来提供 EntityManager 实例
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBeanOne(
                EntityManagerFactoryBuilder builder) {
            return builder.dataSource(dsOne) //配置数据源
                    .properties(jpaProperties.getProperties())//设置 JPA 相关配置
                    .packages("com.zhj.domain")//设置实体类所在的位置
                    .persistenceUnit("pu1")//配置持久化单元名。若项目中只有一个 EntityManagerFactory,则 persistenceUnit 可以省略掉,若有多个,则必须明确指定持久化单元名。
                    .build();
        }
    
        //创建一个事务管理器。JpaTransactionManager 提供对单个 EntityManagerFactory 的事务支持,专门用于解决 JPA 中的事务管理
        @Bean
        PlatformTransactionManager platformTransactionManagerOne(
                EntityManagerFactoryBuilder builder) {
            LocalContainerEntityManagerFactoryBean factoryBeanOne
                    = entityManagerFactoryBeanOne(builder);
            return new JpaTransactionManager(factoryBeanOne.getObject());
        }
    
    }
    

    类似的创建 JpaConfigTwo,注意 去掉 @Primary

  5. 然后 建立相关的mapper 包 ,正常创建 mapper 类即可

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值