package com.malk.config.mutilSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
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.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.Map;
import java.util.Objects;

/**
 * 主数据源配置，多数据源必须设置一个主数据源, 通过 @Primary 注解 [包含公共部分]
 * -
 * 主子项目, 在多数据源情况下, 子项目需要匹配主项目目录结构, 且符合命名规范. 配置对应扫描 EnableJpaRepositories / EntityScan 添加子项目路径
 */
@ConditionalOnProperty(name = "spel.multiSource", havingValue = "true")
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "entityManagerFactoryPrimary", // 配置连接工厂 entityManagerFactory
        transactionManagerRef = "transactionManagerPrimary", // 配置事物管理器  transactionManager
        basePackages = {"com.malk.repository.dao.primary", "com.malk.*.repository.dao.primary", "com.malk.base"}  // dao层配置主数据 & 公共所在目录 [子项目可以访问到主项目primary]
)
public class PrimaryConfig {

    @Autowired
    @Qualifier("primaryDataSource")  // 指定这是主数据源，为了和从(其他)数据源区别开，因为@Autowired不能导入名称相同的是bean
    private DataSource dataSourcePrimary;

    @Autowired
    private JpaProperties jpaProperties;

    @Autowired
    private HibernateProperties hibernateProperties;

    @Primary
    @Bean("entityManagerPrimary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return Objects.requireNonNull(entityManagerFactoryBean(builder).getObject()).createEntityManager();
    }

    @Primary
    @Bean("entityManagerFactoryPrimary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(dataSourcePrimary)
                .properties(getVendorProperties())
                // 设置实体类所在目录: 包含主数据源与公共 [子项目可以访问到主项目primary]
                .packages("com.malk.repository.entity.primary", "com.malk.*.repository.entity.primary", "com.zhuogao.base")
                // 持久化单元名称，当存在多个EntityManagerFactory时，需要制定此名称
                .persistenceUnit("primaryPersistenceUnit")
                .build();
    }

    private Map<String, Object> getVendorProperties() {
        return hibernateProperties.determineHibernateProperties(
                jpaProperties.getProperties(),
                new HibernateSettings()
        );
    }

    @Primary
    @Bean("transactionManagerPrimary")
    public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(Objects.requireNonNull(entityManagerFactoryBean(builder).getObject()));
    }
}

