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選項,因此會自動建庫。ORACLE和SQL_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>