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:\phpstudy\mysql\bin 進入到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:\hxpinstall\mysql\mysql-master
# 設置mysql數據庫的數據的存放目錄,這里可以修改
datadir=D:\hxpinstall\mysql\mysql-master\mydata
# 允許最大連接數
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:\hxpinstall\mysql\mysql-slave1
# 設置mysql數據庫的數據的存放目錄,這里可以修改
datadir=D:\hxpinstall\mysql\mysql-slave1\mydata
# 允許最大連接數
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:\hxpinstall\mysql\mysql-slave2
# 設置mysql數據庫的數據的存放目錄,這里可以修改
datadir=D:\hxpinstall\mysql\mysql-slave2\mydata
# 允許最大連接數
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:\hxpinstall\mysql\mysql-slave1\bin>mysqld --initialize

D:\hxpinstall\mysql\mysql-slave1\bin>
D:\hxpinstall\mysql\mysql-slave1\bin>mysqld --install MySQLSlave1 --defaults-file=D:\hxpinstall\mysql\mysql-slave1\my.ini
Service successfully installed.

D:\hxpinstall\mysql\mysql-slave1\bin>net start MySQLSlave1
MySQLSlave1 服務正在啟動 .
MySQLSlave1 服務已經啟動成功。


D:\hxpinstall\mysql\mysql-slave1\bin>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:\hxpinstall\mysql\mysql-slave1\bin>cd ../../

D:\hxpinstall\mysql>cd mysql-slave2\bin

D:\hxpinstall\mysql\mysql-slave2\bin>mysqld --initialize

D:\hxpinstall\mysql\mysql-slave2\bin>mysqld --install MySQLSlave2 --defaults-file=D:\hxpinstall\mysql\mysql-slave2\my.ini
Service successfully installed.

D:\hxpinstall\mysql\mysql-slave2\bin>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 status\G;查看主從數據庫狀態。此時主庫(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 status\G;
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 status\G;

 

 再次檢查設置過程

在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實現讀寫分離!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM