zoukankan      html  css  js  c++  java
  • springboot整合jpa和mybatis实现主从复制

    百度多方参考终于配出我自己的了,以下仅供参考

    参考https://www.cnblogs.com/cjsblog/p/9712457.html

    代码

    首先数据源配置

    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    #第一个数据源
    spring.datasource.master.jdbc-url=jdbc:mysql://localhost:3307/dab?useUnicode=true&characterEncoding=utf-8
    spring.datasource.master.username=root
    spring.datasource.master.password=root
    #第二个数据源只读账户
    spring.datasource.slave1.jdbc-url=jdbc:mysql://localhost:3308/dab?useUnicode=true&characterEncoding=utf-8
    spring.datasource.slave1.username=root
    spring.datasource.slave1.password=root
    #第三个数据源只读账户
    spring.datasource.slave2.jdbc-url=jdbc:mysql://localhost:3309/dab?useUnicode=true&characterEncoding=utf-8
    spring.datasource.slave2.username=root
    spring.datasource.slave2.password=root
    
    
    
    #连接池参数,初始数、最大数、最小数、获取连接等待超时时间(毫秒)
    spring.datasource.initial-size=5
    spring.datasource.max-active=20
    spring.datasource.min-idle=5
    spring.datasource.max-wait=60000

    启动类

    我的dao和mapper分开的dao放的JPA的持久层,mapper放的mybatis的持久层

    @EnableSwagger2
    @SpringBootApplication
    @EnableJpaRepositories(basePackages = { "com.bling.dab.dao" }, entityManagerFactoryRef = "entityManagerFactoryPrimary", transactionManagerRef = "transactionManagerPrimary")
    @EnableTransactionManagement
    @MapperScan("com.bling.dab.mapper")
    public class DabApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DabApplication.class, args);
            System.out.println("dab启动完成--------OK!");
        }
    
    }

    多数据源jpa和mybatis 配置

    jpa需要比mybatis多自定义一个配置类JpaEntityManager,其中的俩个bean需要在这里依赖,这个注解也可以写在JpaEntityManager配置类上,我写在启动类上了效果一样

    @EnableJpaRepositories(basePackages = { "com.bling.dab.dao" }, entityManagerFactoryRef = "entityManagerFactoryPrimary", transactionManagerRef = "transactionManagerPrimary")
    /**
     * @author: hxp
     * @date: 2019/6/18 10:54
     * @description:
     */
    @Configuration
    @EnableConfigurationProperties(JpaProperties.class)
    @AutoConfigureAfter(DataSourceConfig.class)
    public class JpaEntityManager {
    
        @Autowired
        private JpaProperties jpaProperties;
    
        @Autowired
        private DataSource myRoutingDataSource;
    
        @Bean(name = "entityManagerFactoryBean")
        public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
            Map<String, String> properties = jpaProperties.getProperties();
            //要设置这个属性,实现 CamelCase -> UnderScore 的转换
            properties.put("hibernate.physical_naming_strategy",
                    "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
    
    
            return builder
                    .dataSource(myRoutingDataSource)
                    .properties(properties)
                    .packages("com.bling.dab.domain")
                    .persistenceUnit("myPersistenceUnit")
                    .build();
        }
    
        @Primary
        @Bean(name = "entityManagerFactoryPrimary")
        public EntityManagerFactory entityManagerFactory(EntityManagerFactoryBuilder builder) {
            return this.entityManagerFactoryBean(builder).getObject();
        }
    
        @Primary
        @Bean(name = "transactionManagerPrimary")
        public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
            return new JpaTransactionManager(entityManagerFactory(builder));
        }
    
    }

    然后是mybati和jpa都需要的数据源配置

    @Configuration
    public class DataSourceConfig {
    
        @Bean(name = "masterDataSource")
        @Primary
        @ConfigurationProperties(prefix = "spring.datasource.master")
        public DataSource masterDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "slave1DataSource")
        @ConfigurationProperties(prefix = "spring.datasource.slave1")
        public DataSource slave1DataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "slave2DataSource")
        @ConfigurationProperties(prefix = "spring.datasource.slave2")
        public DataSource slave2DataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "myRoutingDataSource")
        public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                              @Qualifier("slave1DataSource") DataSource slave1DataSource,
                                              @Qualifier("slave2DataSource") DataSource slave2DataSource) {
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
            targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource);
            targetDataSources.put(DBTypeEnum.SLAVE2, slave2DataSource);
            MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
            myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
            myRoutingDataSource.setTargetDataSources(targetDataSources);
            return myRoutingDataSource;
        }
    
    
    }

    轮询数据源使用的类DBContextHolder

    public class DBContextHolder {
    
        private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();
    
        private static final AtomicInteger counter = new AtomicInteger(-1);
    
        public static void set(DBTypeEnum dbType) {
            contextHolder.set(dbType);
        }
    
        public static DBTypeEnum get() {
            return contextHolder.get();
        }
    
        public static void master() {
            set(DBTypeEnum.MASTER);
            System.out.println("切换到master");
        }
    
        public static void slave() {
            //  轮询
            int index = counter.getAndIncrement() % 2;
            if (counter.get() > 9999) {
                counter.set(-1);
            }
            if (index == 0) {
                set(DBTypeEnum.SLAVE1);
                System.out.println("切换到slave1");
            }else {
                set(DBTypeEnum.SLAVE2);
                System.out.println("切换到slave2");
            }
        }
    }

    单独定义的MyRoutingDataSource,使用的是spring提供的路由方法,因为多个地方使用,所以单独定义的

    public class MyRoutingDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DBContextHolder.get();
        }
    }

    mybatis需要的配置类MyBatisConfig

    @EnableTransactionManagement
    @Configuration
    public class MyBatisConfig {
    
        @Resource(name = "myRoutingDataSource")
        private DataSource myRoutingDataSource;
    
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
            sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
            return sqlSessionFactoryBean.getObject();
        }
    
        @Bean
        public PlatformTransactionManager platformTransactionManager() {
            return new DataSourceTransactionManager(myRoutingDataSource);
        }

    切面类实现方法级别的读写分离

    @Aspect
    @Component
    public class DataSourceAspect {
    
        @Pointcut("!@annotation(com.bling.dab.common.annotation.Master) " +
                "&& (execution(* com.bling.dab.service..*.select*(..)) " +
                "|| execution(* com.bling.dab.service..*.get*(..)))"+
                "|| execution(* com.bling.dab.service..*.find*(..)))"+
                "|| execution(* com.bling.dab.service..*.query*(..)))")
        public void readPointcut() {
    
        }
    
        @Pointcut("@annotation(com.bling.dab.common.annotation.Master) " +
                "|| execution(* com.bling.dab.service..*.insert*(..)) " +
                "|| execution(* com.bling.dab.service..*.add*(..)) " +
                "|| execution(* com.bling.dab.service..*.save*(..)) " +
                "|| execution(* com.bling.dab.service..*.update*(..)) " +
                "|| execution(* com.bling.dab.service..*.edit*(..)) " +
                "|| execution(* com.bling.dab.service..*.delete*(..)) " +
                "|| execution(* com.bling.dab.service..*.remove*(..))")
        public void writePointcut() {
    
        }
    
        @Before("readPointcut()")
        public void read() {
            DBContextHolder.slave();
        }
    
        @Before("writePointcut()")
        public void write() {
            DBContextHolder.master();
        }
    }

    注解配合切面类使用

     * @description:该注解标注的就读主库
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Master {
    
    }

    枚举一个

    public enum DBTypeEnum {
    
        MASTER, SLAVE1, SLAVE2;
    }

     mysql数据库配置

    MySQL用的是本地的只有一台服务器,所以需要配置三个MySQL服务器,原来只有一个,现在改一下新安装了三个mysql服务器MySQLMaster、MySQLSlave1、MySQLSlave2用来测试读写分离,原来的MySQL服务不动

    首先cmd进入到MySQL命令窗口

    1.打开【开始】》【运行】输入【cmd】单击【确定】后出现CMD命令黑色窗口,这就是我们说的CMD命令行,或者使用快捷键Windows键(在键盘上有个Windows标志的按键)+R输入cmd后回车。

    2.在CMD命令窗口敲入命令 mysql -hlocalhost -uroot -p

    后按回车(注意这里的"-h"、"-u"、"-p"不能省略) 进入mysql数据库,其中"-h"表示服务器名,localhost表示本地;"-u"为数据库用户名,root是MySQL默认用户名;"-p"为密码,如果设置了密码,可直接在-p后链接输入,如:-p888888,用户没有设置密码,显示Enter password时,直接回车即可。

    注意如果您的MySQL没有安装在C盘下,先使用DOS命令进入MySQL的安装目录下的bin目录中。方法如下:输入G:进入G盘),在输入cd G:phpstudymysqlin 进入到MySQL的bin目录下,才可以输入 mysql -hlocalhost -uroot -p 然后按回车键。

     查看mysql版本并进入mysql命令窗口,现在只有一个mysql,配置了环境变量所以在c目录下也能找到,先查看一下原来的MySQL服务版本,后面开始配置新的主从数据库用来实现读写分离库

    查看mysql的安装路径

     原有的MySQL-5.7.24-winx64拷贝三份到新建的目录mysql下

    结构如图

    修改配置文件my.ini,拷贝的mysql中有个文件aotu.cnf记录的MySQL的uuid需要改一下,不能相同

    master

    [mysql]
    # 设置mysql客户端默认字符集
    default-character-set=utf8 
    [mysqld]
    #设置3306端口
    port = 3307 
    # 设置mysql的安装目录 这里可以修改
    basedir=D:hxpinstallmysqlmysql-master
    # 设置mysql数据库的数据的存放目录,这里可以修改
    datadir=D:hxpinstallmysqlmysql-mastermydata
    # 允许最大连接数
    max_connections=200
    # 服务端使用的字符集默认为8比特编码的latin1字符集
    character-set-server=utf8
    # 创建新表时将使用的默认存储引擎
    default-storage-engine=INNODB
    
    #给数据库服务的唯一标识,一般为大家设置服务器Ip的末尾号,在一个集群中,这个id是不能重复的
    server-id=1
    #开启二进制文件;后面设置的这个master-bin就是二进制文件的名字前缀(名字)
    log-bin=master-bin
    #开启二进制文件的索引;名字一般为log-bin.index
    log-bin-index=master-bin.index
    [client]
    #设置mysql客户端连接服务端时默认使用的端口
    port = 3307
    default-character-set=utf8

    slave1

    [mysql]
    # 设置mysql客户端默认字符集
    default-character-set=utf8 
    [mysqld]
    #设置3306端口
    port = 3308 
    # 设置mysql的安装目录 这里可以修改
    basedir=D:hxpinstallmysqlmysql-slave1
    # 设置mysql数据库的数据的存放目录,这里可以修改
    datadir=D:hxpinstallmysqlmysql-slave1mydata
    # 允许最大连接数
    max_connections=200
    # 服务端使用的字符集默认为8比特编码的latin1字符集
    character-set-server=utf8
    # 创建新表时将使用的默认存储引擎
    default-storage-engine=INNODB
    
    server-id=2
    relay-log=slave-relay-bin 
    relay-log-index=slave-relay-bin.index
    
    [client]
    #设置mysql客户端连接服务端时默认使用的端口
    port = 3308
    default-character-set=utf8 

    slave2

    [mysql]
    # 设置mysql客户端默认字符集
    default-character-set=utf8 
    [mysqld]
    #设置3306端口
    port = 3309 
    # 设置mysql的安装目录 这里可以修改
    basedir=D:hxpinstallmysqlmysql-slave2
    # 设置mysql数据库的数据的存放目录,这里可以修改
    datadir=D:hxpinstallmysqlmysql-slave2mydata
    # 允许最大连接数
    max_connections=200
    # 服务端使用的字符集默认为8比特编码的latin1字符集
    character-set-server=utf8
    # 创建新表时将使用的默认存储引擎
    default-storage-engine=INNODB
    
    server-id=3 
    relay-log=slave-relay-bin 
    relay-log-index=slave-relay-bin.index
    
    [client]
    #设置mysql客户端连接服务端时默认使用的端口
    port = 3309
    default-character-set=utf8 

    原来的MySQL服务不停,启动新的这三个数据库分别

    测试读写分离的时候用的库,端口号为3307、3308、3309,记得配置环境变量

    启动服务试试看,分别到安装bin目录下,以管理员身份cmd

    执行net start mysql

    查看服务启动情况

    先停掉原来的MySQL服务windows+R

     

    配置mysqlmaster服务时没有指定启动的mysql路径,产生一个多余的服务可以执行命令清除,以下为清除多余的服务并重新启动的过程

    给用户配置主从复制的权限:

    mysql> grant replication slave on *.* to 'root'@'%' identified by 'root';
    Query OK, 0 rows affected, 1 warning (0.01 sec)

    注:

    @后面的ip地址为允许连接的客户端的ip地址,如果改为 ‘%’,就表示客户端没有ip地址的限制
    然后查看主服务master的状态: (每重启一次mysql服务,mysql-bin的日志就会新建一个,所以重启主服务,从服务必须先停止再重新配置后启动)
    mysql> show master status;
    +-------------------+----------+--------------+------------------+-------------------+
    | File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +-------------------+----------+--------------+------------------+-------------------+
    | master-bin.000004 |      437 | dab          | mysql            |                   |
    +-------------------+----------+--------------+------------------+-------------------+
    1 row in set (0.00 sec)

    注:

    请记住这个File和Position,File是同步给从库的数据库文件,到时候授权给从库时要用

    navicat连接mysqlmaster成功!

    mydata下的*.err文件

    mysqlslave1和2依次安装启动

     图片太费劲,代码放这里

    D:hxpinstallmysqlmysql-slave1in>mysqld --initialize
    
    D:hxpinstallmysqlmysql-slave1in>
    D:hxpinstallmysqlmysql-slave1in>mysqld --install MySQLSlave1 --defaults-file=D:hxpinstallmysqlmysql-slave1my.ini
    Service successfully installed.
    
    D:hxpinstallmysqlmysql-slave1in>net start MySQLSlave1
    MySQLSlave1 服务正在启动 .
    MySQLSlave1 服务已经启动成功。
    
    
    D:hxpinstallmysqlmysql-slave1in>mysql -u root -p
    Enter password: ************
    Welcome to the MySQL monitor.  Commands end with ; or g.
    Your MySQL connection id is 2
    Server version: 5.7.24
    
    Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
    
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
    
    Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
    
    mysql> ALTER user 'root'@'localhost' IDENTIFIED BY 'root';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> exit;
    Bye
    
    D:hxpinstallmysqlmysql-slave1in>cd ../../
    
    D:hxpinstallmysql>cd mysql-slave2in
    
    D:hxpinstallmysqlmysql-slave2in>mysqld --initialize
    
    D:hxpinstallmysqlmysql-slave2in>mysqld --install MySQLSlave2 --defaults-file=D:hxpinstallmysqlmysql-slave2my.ini
    Service successfully installed.
    
    D:hxpinstallmysqlmysql-slave2in>net start MySQLSlave2
    MySQLSlave2 服务正在启动 .
    MySQLSlave2 服务已经启动成功。
    mysql> show master status;
    +-------------------+----------+--------------+------------------+-------------------+
    | File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +-------------------+----------+--------------+------------------+-------------------+
    | master-bin.000005 |      154 |              |                  |                   |
    +-------------------+----------+--------------+------------------+-------------------+
    1 row in set (0.00 sec)
    
    mysql> exit
    Bye

    主从数据库启动成功后,分别执行show master status;和show slave statusG;查看主从数据库状态。此时主库(master)下生成了一个二进制的日志文件,而slave下是空的,所以就要把主库与从库关联起来,即只需要让从库(slave)知道主库(master)的地址就可以了。在从库(slave)执行如下命令,将主库与从库联系起来,然后执行命令start slave开启主从同步。

    change master to master_host='localhost',master_port=3306,master_user='root',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=120;

    开启复制

    start slave

    停止复制

    stop slave

    mysql> show slave statusG;
    Empty set (0.00 sec)
    
    ERROR:
    No query specified

    执行指定主库命令

    mysql> change master to master_host='localhost',master_port=3307,master_user='root',master_password='root',master_log_file='mysql-bin.000005',master_log_pos=154;
    Query OK, 0 rows affected, 2 warnings (0.06 sec)

    执行开启复制

    start slave

    再到mysqlslave2从库下执行一次

    执行开启复制

    start slave

    这样3台新mysql服务器安装成功并且主从数据库关联

    测试一下主从关联是否成功,正在测试。。。

    在从库中执行show slave statusG;

     再次检查设置过程

    在master库中执行刷新log命令,每次刷新,log文件加1

    在进入slave库重新设置

    所以重新来一次对的

    执行show slave status;看看

    测试建表看看

    现在master库执行sql

    CREATE TABLE `login_user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(255) DEFAULT NULL,
      `password` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8;

    查看从库

    如法在slave2设置一下,也成功建表

    最后测试数据读写分离的效果

    保存数据测试类

    写入操作成功切换到master库

    数据库数据也成功写入

     查询看看

    查询测试类

    [10:56:00:654] [INFO] - org.springframework.boot.StartupInfoLogger.logStarted(StartupInfoLogger.java:59) - main - Started DabApplicationTests in 13.68 seconds (JVM running for 15.591)
    切换到slave2
    [10:56:00:793] [INFO] - com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:110) - main - HikariPool-2 - Starting...
    [10:56:00:818] [INFO] - com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123) - main - HikariPool-2 - Start completed.
    [10:56:01:008] [INFO] - com.bling.dab.DabApplicationTests.selectByPrimaryKey(DabApplicationTests.java:560) - main - {"code":1,"data":{"id":1,"password":"123456","username":"张大大"},"message":"成功","success":true}

    查询成功

    OK结束

    下次在研究mycat实现读写分离!

  • 相关阅读:
    浅谈localStorage本地存储
    前端代码中经常遇到的问题
    http状态码
    Transition 所支持的css属性
    ie8下的透明 问题
    #event.initMouseEvent
    svg 文字
    前端工作流程转变
    理解javascript 回调函数
    不要被npm、NodeJs、npm、webpack、vue-cli 这些名词搞晕
  • 原文地址:https://www.cnblogs.com/h-c-g/p/11050334.html
Copyright © 2011-2022 走看看