APDPlat如何自動建庫建表並初始化數據?

APDPlat共支持10種數據庫:DB二、DERBY、H二、HSQL、INFORMIX、MYSQL、ORACLE、POSTGRESQL、SQL_SERVER、SYBASE。java

 

數據庫的默認配置信息在文件APDPlat_Core/src/main/resources/org/apdplat/db.properties中定義,用戶能夠根據本身的選擇,在APDPlat_Web/src/main/resources/db.local.properties配置文件中覆蓋默認配置。mysql

 

一、如何指定使用哪種數據庫呢?git

 

jpa.database=MYSQL

 

 

jpa.database配置項的值可爲上述10種數據庫之一,10種數據庫的JDBC驅動已經集成到APDPlat中,其中5種定義到maven配置文件APDPlat_Web/pom.xml的依賴中,其他5种放置在APDPlat_Web/src/main/webapp/WEB-INF/lib目錄中。github

 

二、如何配置數據庫鏈接信息呢? web

 

#mysql
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/${module.short.name}?useUnicode=true&characterEncoding=UTF-8&createDatabaseIfNotExist=true&autoReconnect=true
db.username=root
db.password=root
jpa.database=MYSQL
db.backup.command=mysqldump  -u${db.username} -p${db.password} ${module.short.name}
db.restore.command=mysql -u${db.username} -p${db.password} ${module.short.name}

db.forlog.driver=com.mysql.jdbc.Driver
db.forlog.url=jdbc:mysql://localhost:3306/${module.short.name}_for_log?useUnicode=true&characterEncoding=UTF-8&createDatabaseIfNotExist=true&autoReconnect=true
db.forlog.username=root
db.forlog.password=root
jpa.forlog.database=MYSQL

  

 

在文件APDPlat_Core/src/main/resources/org/apdplat/db.properties中已經預先定義了10種數據庫的鏈接信息,用戶可在此基礎上修改爲適合本身的鏈接信息,而後放置到配置文件APDPlat_Web/src/main/resources/db.local.properties中覆蓋默認配置。spring

 

對於MYSQL數據庫來講,由於指定了createDatabaseIfNotExist=true選項,因此會自動建庫ORACLESQL_SERVER須要手動建庫。其餘數據庫請參考相關的JDBC編程指南文檔,總之,在這裏只須要提供標準的JDBC編程所需的信息:driver、url、username、password。sql

 

三、表是怎麼建的呢?數據庫

 

表是自動建的,不須要手動執行數據庫DDL,是由以下配置指定的:編程

 

jpa.generateDdl=true

 

 

在文件APDPlat_Core/src/main/resources/org/apdplat/db.properties中還定義了數據庫鏈接池以及數據庫緩存的配置信息,詳情參考配置文件。設計模式

 

四、初始數據是怎麼導入的?

 

初始數據是在系統啓動的時候自動導入的,不須要手工執行SQL腳本導入數據。RegisterService抽象類爲數據導入提供了支持,在Spring容器初始完畢後就會檢查是否須要導入數據,只有在表爲空的狀況下才會執行導入,也就是說可以保證只會導入一次,執行導入調用的是抽象方法registe,須要子類來實現,這是一個典型的"模板方法"設計模式:

 

public abstract class RegisterService<T extends Model> implements ApplicationListener {
    protected final APDPlatLogger LOG = APDPlatLoggerFactory.getAPDPlatLogger(getClass());
    
    @Resource(name="serviceFacade")
    protected ServiceFacade serviceFacade;
    @Resource(name="entityManagerFactory")
    protected EntityManagerFactory entityManagerFactory;
    
    protected Class<T> modelClass;
    @Override
    public void onApplicationEvent(ApplicationEvent event){
        if(event instanceof ContextRefreshedEvent){
            this.modelClass = ReflectionUtils.getSuperClassGenricType(getClass());
            LOG.info("spring容器初始化完成, 開始檢查 "+ModelMetaData.getMetaData(this.modelClass.getSimpleName()) +" 是否須要初始化數據");
            if(shouldRegister()){
                LOG.info("須要初始化 "+ModelMetaData.getMetaData(this.modelClass.getSimpleName()));
                openEntityManager();
                registe();
                closeEntityManager();
                registeSuccess();
            }else{
                LOG.info("不須要初始化 "+ModelMetaData.getMetaData(this.modelClass.getSimpleName()));
            }
        }
    }
    private void openEntityManager(){        
        EntityManager em = entityManagerFactory.createEntityManager();
        TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(em));
        LOG.info("打開實體管理器");
    }
    private void closeEntityManager(){
        EntityManagerHolder emHolder = (EntityManagerHolder)TransactionSynchronizationManager.unbindResource(entityManagerFactory);
        LOG.info("關閉實體管理器");
        EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
    }
    protected void registeSuccess(){
        
    }
    protected List<T> getRegisteData(){
        return null;
    }
    protected abstract void registe();

    protected boolean shouldRegister() {
        Page<T> page=serviceFacade.query(modelClass);
        if(page.getTotalRecords()==0) {
            return true;
        }
        return false;
    }
}

 

 

下面咱們看一個導入用戶數據(至少數據庫中有用戶數據才能登錄吧)的例子,這裏RegisteUser 實現了抽象類RegisterService的抽象方法registe,執行真正的數據導入操做。從這裏咱們能夠看到,用戶數據是從類路徑下的/data/user.xml文件中得到的。事實上,這個文件是在APDPlat啓動的時候從APDPlat_Core-X.X.jar中提取出來的。這裏使用了Page的newInstance靜態方法把一個XML數據文件轉換爲了JAVA頁面對象。以後設置用戶數據的依賴,即用戶所屬的組織架構和角色,注意這裏的導入是有前後順序的,須要保證順序。

 

/**
 * 註冊用戶數據,這裏須要注意的是:
 * 由於User有一個Org字段和一個Role列表(protected Org org; protected List<Role> roles = new ArrayList<>();)
 * 因此要先註冊了Org和Role以後才能註冊用戶
 * 可是RegisteUser、RegisteOrg以及RegisteRole都繼承自RegisterService
 * 都實現了Spring的ApplicationListener接口
 * 那麼Spring會先調用誰呢?
 * 爲了保證先註冊Org和Role
 * RegisteUser類用@Resource註解注入了RegisteOrg和RegisteRole,分別用來獲取Org和Role
 * Spring保證會在裝配完成RegisteOrg和RegisteRole以後才裝配RegisteUser
 * 而裝配的前後順序也就是Spring調用實現ApplicationListener接口的Bean的順序
 * 所以,這裏的註冊數據依賴問題就完美地解決了
 * @author 楊尚川
 */
@Service
public class RegisteUser extends RegisterService<User>{
    @Resource(name="registeOrg")
    protected RegisteOrg registeOrg;
    @Resource(name="registeRole")
    protected RegisteRole registeRole;

    @Override
    protected void registe() {
        String xml="/data/user.xml";
        LOG.info("註冊【"+xml+"】文件");
        LOG.info("驗證【"+xml+"】文件");
        boolean pass=XMLUtils.validateXML(xml);
        if(!pass){
            LOG.info("驗證沒有經過,請參考dtd文件");
            return ;
        }
        LOG.info("驗證經過");
        Page<User> page=Page.newInstance(User.class, RegisteUser.class.getResourceAsStream(xml));
        if(page!=null){
            for(User user : page.getModels()){
                user.setPassword(PasswordEncoder.encode(user.getPassword(), user));
                user.setOrg(registeOrg.getRegisteData().get(0));
                user.addRole(registeRole.getRegisteData().get(0).getChild().get(0));
                serviceFacade.create(user);
            }
        }
    }
}

 

 

最後看一下數據文件/data/user.xml的內容:

 

 

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page SYSTEM  "user.dtd">
<!--
        用戶
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <models>
        <model xsi:type="User" username="admin" password="admin" enabled="true" des="超級管理員"/>
    </models>
</page>

 

 

 APDPlat託管在Github

相關文章
相關標籤/搜索