作者:小傅哥
部落格:https://bugstack.cn
原始碼:https://github.com/fuzhengwei/CodeGuide/wiki
沉澱、分享、成長,讓自己和他人都能有所收穫!?
一、前言
都能用,都能湊活用!
一個東西好幾套,為了晉升都來搞。拿了成績就要跑,後面兄弟再重造!
幾年前,大家並不是這樣,那時候還有很多東西可以創新,亂世出英雄總能在一個方向深耕並作出一款款好用的產品功能、框架服務、技術元件等。但後來好像這樣的情況開始減少了,取而代之的是重複、復刻、照搬,換個新的皮膚、換個新的樣式、換個新的名字,就是取巧的新東西了。
有時候公司或者組織也像家,但家裡的東西一般是破了補補、壞了修修,實在不行就換個,但沒有誰的家裡衛生間一個馬桶、廚房一個馬桶、客廳一個馬桶、臥室一個馬桶的,雖然你的新馬桶可以自動噴水。
所以,在建設一個好的產品功能時,儘可能要學學那些已經非常的優秀的產品,IDEA、GitHub、Mysql等等,在IDEA提供了滿足使用者擴充套件功能的外掛開發,而不是你說一個東西我沒有,你就自己造。共建會讓這個東西變得更加優秀!
二、需求目的
在上一章節中我們通過擴充套件建立工程嚮導,新增我們需要建立DDD工程腳手架的步驟,最終提供一個DDD開發框架。那麼在這個DDD工程開發框架中,還缺少一部分基於資料庫表資訊自動生成對應PO、DAO、Mapper檔案的功能。
- 那麼本章節我們就來在工程中擴充套件這部分內容,實際操作的效果就是我們可以在工程上通過滑鼠右鍵的方式,喚出新增ORM程式碼塊的窗體,通過選擇庫表的方式,使用 freemarker 自動生成程式碼。
- 在生成的程式碼塊中需要完成對所需要包的引入,同時會使用到 lombok 註解的方式替代PO物件中的get、set方法,以減少程式碼量邏輯的建立。
三、案例開發
1. 工程結構
guide-idea-plugin-orm
├── .gradle
└── src
├── main
│ └── java
│ └── cn.bugstack.guide.idea.plugin
│ ├── action
│ │ └── CodeGenerateAction.java
│ ├── domain
│ │ ├── model.vo
│ │ │ ├── CodeGenContextVO.java
│ │ │ └── ORMConfigVO.java
│ │ └── service
│ │ ├── impl
│ │ │ └── ProjectGeneratorImpl.java
│ │ ├── AbstractProjectGenerator.java
│ │ ├── GeneratorConfig.java
│ │ └── IProjectGenerator.java
│ ├── infrastructure
│ │ ├── data
│ │ │ ├── DataSetting.java
│ │ │ └── DataState.java
│ │ ├── po
│ │ │ ├── Base.java
│ │ │ ├── Column.java
│ │ │ ├── Dao.java
│ │ │ ├── Field.java
│ │ │ ├── Model.java
│ │ │ └── Table.java
│ │ └── utils
│ │ ├── DBHelper.java
│ │ └── JavaType.java
│ ├── module
│ │ └── FileChooserComponent.java
│ └── ui
│ ├── ORMSettingsUI.java
│ └── ORMSettingsUI.form
├── resources
│ ├── META-INF
│ │ └── plugin.xml
│ └── template
│ ├── dao.ftl
│ ├── mapper.ftl
│ └── model.ftl
├── build.gradle
└── gradle.properties
原始碼獲取:#公眾號:bugstack蟲洞棧
回覆:idea
即可下載全部 IDEA 外掛開發原始碼# 第 5 章:在開發工程中滑鼠右鍵,生成ORM程式碼
在此 IDEA 外掛工程中,主要分為5塊區域:
- action:用於提供選單欄,這個選單的位置在 plugin.xml 中配置,我們把它配置到工程滑鼠右鍵出現的列表上。這樣可以更加方便的讓我們選取工程,以及在這個工程下新增生成的程式碼片段
- domain:領域服務層,其實你直接寫一個Service包也是可以的,只不過最近作者小傅哥更喜歡使用DDD的思想和結構來建立程式碼實現功能邏輯。
- infrastructure:基礎層,提供資料在工程下的存放,每一個工程右鍵都有自己的配置儲存預設資訊,方便下次開啟的時候可以讀取到這部分內容。同時這一層還提供了用於處理資料庫操作的類,因為我們需要從資料庫中讀取出表的資訊、欄位、註釋,用於建立PO、DAO、Mapper使用。
- module:模組層,這裡提供了一個用於選擇檔案路徑的元件,可以讓我們在工程上滑鼠右鍵的出來的窗體中,點選模組選擇對應的要生成程式碼的位置路徑。
- ui:提供配置皮膚,也就是我們在程式碼工程上滑鼠右鍵彈出來的皮膚,這個皮膚配置後用於生成ORM程式碼。
2. 拖拽Swing皮膚
ORMSettingsUI:我們們先把用於建立程式碼配置的皮膚建立出來,有了畫面,就好進入了。
- 皮膚包括生成 PO、DAO、XML 的程式碼路徑,以及配置資料庫和選擇表的內容。
- 操作過程就是在你配置好了這些基本資訊後,就可以選擇查詢表名,並選擇好你要給哪幾個表生成對應的ORM程式碼了。
3. 配置滑鼠右鍵彈窗
首先我們需要建立一個 Action 實現類,通過 New -> Plugin DevKit -> Action
cn.bugstack.guide.idea.plugin.action.CodeGenerateAction
/**
* @author: 小傅哥,微信:fustack
* @github: https://github.com/fuzhengwei
* @Copyright: 公眾號:bugstack蟲洞棧 | 部落格:https://bugstack.cn - 沉澱、分享、成長,讓自己和他人都能有所收穫!
*/
public class CodeGenerateAction extends AnAction {
private IProjectGenerator projectGenerator = new ProjectGeneratorImpl();
@Override
public void actionPerformed(AnActionEvent e) {
Project project = e.getRequiredData(CommonDataKeys.PROJECT);
ShowSettingsUtil.getInstance().editConfigurable(project, new ORMSettingsUI(project, projectGenerator));
}
}
- 這是一個右鍵選單的入口,通過這個入口才能去開啟我們自己的UI窗體,這個UI窗體就是我們上面拖拽出來的配置皮膚,ORMSettingsUI
- 接下來我們還需要把這個 Action 配置到 plugin.xml 檔案中,才能被右鍵選單建立出來。開發程式碼的時候也是這樣一個流程,你總要從一個點開始,有了抓手才好抓下去
plugin.xml 配置
<actions>
<!-- Add your actions here -->
<action id="CodeGenerateAction" class="cn.bugstack.guide.idea.plugin.action.CodeGenerateAction"
text="ORMCodeGenerate - 小傅哥" description="Code Generate ORM" icon="/icons/logo.png">
<add-to-group group-id="ProjectViewPopupMenu" anchor="last"/>
</action>
</actions>
ea-plugin>
- 把我們的 Action 實現類配置到 xml 中,同時你還要配置它應該出現的位置,比如你需要把這個選單新增到工程建立中
ProjectViewPopupMenu
以及位置資訊anchor="last"
- 另外為了讓外掛看上去更加高大上還美觀適合吹牛,那麼還需要配置 icon,這個位置配置一個
16*16
的圖片,圖片可以從 iconfont 進行下載。
4. 給窗體新增功能
這一步其實幹的就是注入靈魂的事情,讓窗體活起來。給輸入框新增內容、給按鈕新增事件、給確認按鈕增加上生成建立ORM程式碼塊的上下文。文章的描述儘可能會偏向於核心程式碼的講解,詳情可以參考原始碼
接下來這部分內容會在 ORMSettingsUI 類中反覆摩擦,直到補全所有功能。
4.1 選擇框事件
// 選擇PO生成目錄
this.poButton.addActionListener(e -> {
FileChooserComponent component = FileChooserComponent.getInstance(project);
VirtualFile baseDir = project.getBaseDir();
VirtualFile virtualFile = component.showFolderSelectionDialog("選擇PO生成目錄", baseDir, baseDir);
if (null != virtualFile) {
ORMSettingsUI.this.poPath.setText(virtualFile.getPath());
}
});
- 還記得我們提到的
拖拽Swing皮膚
嗎,那麼這個新增事件的步驟就是給你的 PO 目錄新增一個事件,允許我們可以自己選擇出要把對應PO的程式碼生成到哪個目錄結構下。 - 關於dao、xml都是類似操作,這裡就不在演示了。
4.2 資料表事件
this.selectButton.addActionListener(e -> {
try {
DBHelper dbHelper = new DBHelper(this.host.getText(), Integer.parseInt(this.port.getText()), this.user.getText(), this.password.getText(), this.database.getText());
List<String> tableList = dbHelper.getAllTableName(this.database.getText());
String[] title = {"", "表名"};
Object[][] data = new Object[tableList.size()][2];
for (int i = 0; i < tableList.size(); i++) {
data[i][1] = tableList.get(i);
}
table1.setModel(new DefaultTableModel(data, title));
TableColumn tc = table1.getColumnModel().getColumn(0);
tc.setCellEditor(new DefaultCellEditor(new JCheckBox()));
tc.setCellEditor(table1.getDefaultEditor(Boolean.class));
tc.setCellRenderer(table1.getDefaultRenderer(Boolean.class));
tc.setMaxWidth(100);
} catch (Exception exception) {
Messages.showWarningDialog(project, "資料庫連線錯誤,請檢查配置.", "Warning");
}
});
- 這一步操作核心流程就在於把你需要生成ORM的程式碼的表給拉出來,只要把表選擇上,才能根據表生成PO、DAO、Mapper,其實你用的其他一些自動生成程式碼框架也是這麼幹的。
- 另外你的建表最好規範,比如有表註釋、有欄位註釋、欄位的設計遵守下劃線和小寫字母,這樣會更加容易建立出好看的程式碼。
4.3 組裝生成程式碼上下文
當我們點選配置窗體的 OK 按鈕時候,要幹啥,對嘍,我們要建立出程式碼片段了,那麼這個時候需要在重寫的 apply
中完成此項操作。
public void apply() {
// 連結DB
DBHelper dbHelper = new DBHelper(config.getHost(), Integer.parseInt(config.getPort()), config.getUser(), config.getPassword(), config.getDatabase());
// 組裝程式碼生產上下文
CodeGenContextVO codeGenContext = new CodeGenContextVO();
codeGenContext.setModelPackage(config.getPoPath() + "/po/");
codeGenContext.setDaoPackage(config.getDaoPath() + "/dao/");
codeGenContext.setMapperDir(config.getXmlPath() + "/mapper/");
List<Table> tables = new ArrayList<>();
Set<String> tableNames = config.getTableNames();
for (String tableName : tableNames) {
tables.add(dbHelper.getTable(tableName));
}
codeGenContext.setTables(tables);
// 生成程式碼
projectGenerator.generation(project, codeGenContext);
}
- 在 apply 中的核心程式碼主要就是使用 DBHelper 資料操作工具獲取到對應的庫下連結資訊,同時把選擇的號的表建立出用於生成程式碼類的引數,比如;類的名稱、欄位名稱、註釋名稱等。
- 最後就是呼叫生成程式碼的服務了,
projectGenerator.generation(project, codeGenContext);
這一部分就是在我們領域服務 domain 中實現的。
5. 程式碼生成領域服務
- 用於建立PO、DAO、Mapper的程式碼塊的程式碼主要是這裡實現的,核心在於提供了一個抽象類以及對應的實現類,因為處理程式碼生成需要使用到 freemarker 所以就在抽象類裡包裝了下,這樣可以免去實現類中還需要關心這部分邏輯。
ProjectGeneratorImpl 生成程式碼
@Override
protected void generateORM(Project project, CodeGenContextVO codeGenContext) {
List<Table> tables = codeGenContext.getTables();
for (Table table : tables) {
List<Column> columns = table.getColumns();
List<Field> fields = new ArrayList<>();
for (Column column : columns) {
Field field = new Field(column.getComment(), JavaType.convertType(column.getType()), column.getName());
field.setId(column.isId());
fields.add(field);
}
// 生成PO
Model model = new Model(table.getComment(), codeGenContext.getModelPackage() + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, table.getName()), table.getName(), fields);
writeFile(project, codeGenContext.getModelPackage(), model.getSimpleName() + ".java", "domain/orm/model.ftl", model);
// 生成DAO
Dao dao = new Dao(table.getComment(), codeGenContext.getDaoPackage() + "I" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, table.getName()) + "Dao", model);
writeFile(project, codeGenContext.getDaoPackage(), dao.getSimpleName() + ".java", "domain/orm/dao.ftl", dao);
// 生成Mapper
writeFile(project, codeGenContext.getMapperDir(), dao.getModel().getSimpleName() + "Mapper.xml", "domain/orm/mapper.ftl", dao);
}
}
- 建立程式碼的過程就比較簡單了,通過迴圈提取出來的表資訊,對映成對應的類和屬性以及註釋,再使用 resources 下的 ftl 檔案建立出對應的類和xml配置檔案就可以了。
- 如果你還需要生成起來程式碼片段或者建立呼叫一些常用的元件,也是可以通過這樣的方式進行實現的。
四、測試驗證
- 點選
Plugin
啟動 IDEA 外掛,之後在工程右鍵如下:
1. 滑鼠右鍵,選擇選單
2. 配置頁面,配置資訊
3. 自動建立,生成程式碼
- 好了,選擇程式碼塊就這麼嗖的建立了出來,是不是非常方便,而且可以滿足你在任何時候的把新的庫表程式碼補充進來,減少了手敲CRUD操作。
五、總結
- 本章節小傅哥帶著你又在 IDEA DDD 外掛生成工程的結構下,又完善了一步生成ORM程式碼,當然你也可以在建立工程嚮導中新增生成ORM程式碼的步驟。而在工程下建立ORM的方式可以當做是對腳手架工程的補充,滿足不同場景下的需求。
- 此外在 IDEA 外掛開發的系列內容中我們是不斷的嘗試使用新的方式完善不同的功能點,如果你需要開發一個完整的外掛那麼可以結合這些功能一起來開發你的需求。
- 外掛開發中還是有很多的內容需要了解和學習的,同時也要注意一些程式碼實現細節,例如我們本章節中的資料儲存是在一個什麼維度,是IDEA開發工具維度,還是在IDEA中的工程維度,這些是有區別。比如你的不同工程,是不需要儲存同一份配置的