snmp与java集成_轻松地与Java完全集成
snmp與java集成
這里是如何不使用SQL,HQL,PHP,ASP,HTML,CSS或Javascript而是使用Vaadin的UI層和Speedment Stream ORM完全依賴Java編寫完整堆棧數據庫Web應用程序的方法。
是否曾經想過快速創建連接到您現有數據庫的Web應用程序,或者構建具有較短上市時間要求的專業應用程序? Java Stream API釋放了用純Java編寫數據庫查詢的可能性。
在本文中,我們將演示如何利用兩個Java框架來完成此任務。 Vaadin和Speedment。 由于它們都使用Java Streams,因此很容易將它們連接在一起。 這意味著我們最終將得到一個簡短,簡潔且類型安全的應用程序。
對于此小型項目,我們將使用名為“ Employees”的My SQL示例數據庫,該數據庫提供分布在六個單獨的表上的約160MB數據,包括400萬條記錄。
完整的應用程序代碼可在GitHub上獲得,如果您想在自己的環境中運行該應用程序,則可以克隆此存儲庫。 您還需要Vaadin和Speedment的試用許可證才能使用本文中使用的功能。 這些是免費的。
預期的最終結果是一個Web應用程序,可以在其中分析不同部門之間的性別平衡和工資分配。 使用純標準的Vaadin Charts Java組件以圖形方式顯示結果,如以下視頻所示:
設置數據模型
我們正在使用Speedment Stream ORM訪問數據庫。 使用Speedment初始化程序可以輕松設置任何項目。 Speedment可以直接從數據庫的架構數據生成Java類。 生成后,我們可以像下面這樣創建Speedment實例:
Speedment speedment = new EmployeesApplicationBuilder().withUsername("...") // Username need to match database.withPassword("...") // Password need to match database.build();為部門創建下拉菜單
在我們的Web應用程序中,我們希望有一個所有部門的下拉列表。 從該方法可以很容易地從數據庫中檢索部門:
public Stream<Departments> departments() {DepartmentsManager depts = speedment.getOrThrow(DepartmentsManager.class);return depts.stream(); }將部門和員工聯系在一起
現在,我們將在Departments和Employees之間創建聯接關系。 在數據庫中,有一個多對多關系表,將這些表連接在一起,稱為DeptEmpl 。
首先,我們創建一個自定義元組類,該類將保存聯接表中的三個條目:
public final class DeptEmplEmployeesSalaries {private final DeptEmp deptEmp;private final Employees employees;private final Salaries salaries;public DeptEmplEmployeesSalaries(DeptEmp deptEmp, Employees employees, Salaries salaries) {this.deptEmp = requireNonNull(deptEmp);this.employees = requireNonNull(employees);this.salaries = requireNonNull(salaries);}public DeptEmp deptEmp() { return deptEmp; }public Employees employees() { return employees; }public Salaries salaries() { return salaries; }public static TupleGetter0 deptEmpGetter() {return DeptEmplEmployeesSalaries::deptEmp;}public static TupleGetter1 employeesGetter() {return DeptEmplEmployeesSalaries::employees;}public static TupleGetter2 salariesGetter() {return DeptEmplEmployeesSalaries::salaries;}}DeptEmplEmployeesSalaries只是這三個實體的不可變持有人,除了它具有可用于提取單個實體的三個附加“ getter”方法。 請注意,它們返回TupleGetter ,與僅使用匿名lambda或方法引用相比,它們允許聯接和聚合使用優化版本。
現在有了自定義元組,我們可以輕松定義Join關系:
private Join joinDeptEmpSal(Departments dept) {// The JoinComponent is needed when creating joinsJoinComponent jc = speedment.getOrThrow(JoinComponent.class);return jc.from(DeptEmpManager.IDENTIFIER)// Only include data from the selected department.where(DeptEmp.DEPT_NO.equal(dept.getDeptNo()))// Join in Employees with Employees.EMP_NO equal DeptEmp.EMP_NO.innerJoinOn(Employees.EMP_NO).equal(DeptEmp.EMP_NO)// Join Salaries with Salaries.EMP_NO) equal Employees.EMP_NO.innerJoinOn(Salaries.EMP_NO).equal(Employees.EMP_NO)// Filter out historic salary data.where(Salaries.TO_DATE.greaterOrEqual(currentDate)).build(DeptEmplEmployeesSalaries::new);}在構建Join表達式時,我們首先使用DeptEmp表開始(我們記得,這是Departments和Employees之間的多對多關系表)。 對于此表,我們應用where()語句,以便我們僅能過濾出屬于我們要在聯接中顯示的部門的那些多對多關系。
接下來,我們聯接Employees表,并指定聯接關系,其中新聯接的表的Employees.EMP_NO列等于DeptEmp.EMP_NO 。
之后,我們加入Salaries表并指定另一個聯接關系,其中Salaries.EMP_NO等于Employees.EMP_NO 。 對于此特定的聯接關系,我們還應用where()語句,以便我們過濾掉當前的薪水(而不是歷史的,雇員的過去薪水)。
最后,我們調用build()方法并定義DeptEmplEmployeesSalaries類的構造函數,該類包含三個實體DeptEmp , Employees和Salaries 。
計算部門的員工人數
使用上面的join方法,可以很容易地在Join流中計算某個部門的員工人數。 我們可以這樣去做:
public long countEmployees(Departments department) {return joinDeptEmpSal(department).stream().count(); }計算工資分配匯總
通過使用內置的Speedment Aggregator,我們可以非常輕松地表達聚合。 聚合器可以使用常規Java集合,單個表中的Java流以及連接流,而無需在堆上構造中間Java對象。 這是因為它完全不在堆中存儲所有數據結構。
我們首先以簡單的POJO形式創建“結果對象”,該POJO將用作完成的堆外聚合與Java堆世界之間的橋梁:
public class GenderIntervalFrequency {private Employees.Gender gender;private int interval;private long frequency;private void setGender(Employees.Gender gender) { this.gender = gender; }private void setInterval(int interval) { this.interval = interval; }private void setFrequency(long frequency) { this.frequency = frequency;}private Employees.Gender getGender() { return gender; }private int getInterval() { return interval; }private long getFrequency() { return frequency; }}現在有了POJO,我們可以構建一個返回像這樣的Aggregation的方法:
public Aggregation freqAggregation(Departments dept) {Aggregator aggregator =// Provide a constructor for the "result object"Aggregator.builder(GenderIntervalFrequency::new)// Create a key on Gender.firstOn(DeptEmplEmployeesSalaries.employeesGetter()).andThen(Employees.GENDER).key(GenderIntervalFrequency::setGender)// Create a key on salary divided by 1,000 as an integer.firstOn(DeptEmplEmployeesSalaries.salariesGetter()).andThen(Salaries.SALARY.divide(SALARY_BUCKET_SIZE).asInt()).key(GenderIntervalFrequency::setInterval)// For each unique set of keys, count the number of entitites.count(GenderIntervalFrequency::setFrequency).build();return joinDeptEmpSal(dept).stream().parallel().collect(aggregator.createCollector());}這需要一些解釋。 當我們調用Aggregator.builder()方法時,我們提供了“結果對象”的構造函數,我們將其用作堆外世界與堆上世界之間的橋梁。
有了構建器之后,我們可以開始定義聚合,通常最簡單的方法是從將在聚合中使用的鍵(即組)開始。 在匯總Join操作的結果時,我們首先需要指定要從中提取密鑰的實體。 在這種情況下,我們要使用員工的性別,因此我們調用.firstOn(eptEmplEmployeesSalaries.employeesGetter()) ,它將從元組中提取Employees實體。 然后,我們應用.andThen(Employees.GENDER) ,這將反過來從Employees實體中提取性別屬性。 key()方法為要實際讀取聚合結果的方法將調用的方法提供方法參考。
第二個鍵的指定方式幾乎相同,只是在這里我們應用.firstOn(DeptEmplEmployeesSalaries.salariesGetter())方法來提取Salaries實體而不是Employees實體。 然后,當我們應用.andThen()方法時,我們正在使用一個表達式來轉換薪水,因此它被除以1,000,并被視為整數。 這將為薪水的每千美元創建一個單獨的收入等級。
count()運算符只是說我們要計算每個密鑰對的出現次數。 因此,如果有兩個男性的收入在57級(即57,000到57,999之間的薪水),則計數操作將計算這兩個密鑰。
最后,在以return開頭的行中,將進行匯總的實際計算,由此應用程序將并行匯總所有成千上萬的薪水,并返回數據庫中所有收入數據的Aggregation 。 可以將Aggregation視為具有所有鍵和值的一種List ,僅將數據存儲在堆外。
添加JVM中的內存加速
通過在我們的應用程序中增加兩行,我們可以獲得具有in-JVM內存加速的高性能應用程序。
Speedment speedment = new EmployeesApplicationBuilder().withUsername("...") // Username need to match database.withPassword("...") // Password need to match database.withBundle(InMemoryBundle.class) // Add in-JVM-acceleration.build();// Load a snapshot of the database into off-heap JVM-memoory speedment.get(DataStoreComponent.class) .ifPresent(DataStoreComponent::load);InMemoryBundle允許使用堆外內存將整個數據庫引入JVM,然后允許直接從RAM而不是使用數據庫直接執行Streams和Joins。 這將提高性能,并使Java應用程序更具確定性。 將數據放到堆外還意味著數據不會影響Java Garbage Collect,從而可以使用巨大的JVM,而不會影響GC。
由于有了內存中的加速功能,即使在我的筆記本電腦上,即使最大的部門擁有超過60,000薪水,也可以在不到100毫秒的時間內計算出來。 這將確保我們的UI保持響應狀態。
用Java構建UI
現在,數據模型已經完成,我們繼續進行應用程序的可視化方面。 如前所述,這是利用Vaadin完成的,該框架允許使用Java實現HTML5 Web用戶界面。 Vaadin框架建立在組件的概念上,這些組件可以是布局,按鈕或介于兩者之間的任何東西。 組件被建模為對象,可以以多種方式對其進行自定義和樣式設置。
上圖描述了我們打算為DataModel構建的GUI的結構。 它由9個組件組成,其中5個組件是從數據庫中讀取信息并將其呈現給用戶的,其余的則是靜態的。 事不宜遲,讓我們開始配置UI。
此圖顯示了GUI中包含的組件的層次結構。
Vaadin UI層
為了將Vaadin集成到應用程序中,我們從Vaadin下載了一個入門包,以建立一個簡單的項目基礎。 這將自動生成一個UI類,該類是任何Vaadin應用程序的基礎。
@Theme("mytheme") public class EmployeeUI extends UI {@Override // Called by the server when the application startsprotected void init(VaadinRequest vaadinRequest) { }// Standard Vaadin servlet which was not modified @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)@VaadinServletConfiguration(ui = EmployeeUI.class, productionMode = false)public static class MyUIServlet extends VaadinServlet { } }當應用程序啟動時,從服務器調用了覆蓋的init() ,因此,在此我們將很快聲明在應用程序運行時將執行哪些操作。 EmployeeUI還包含MyUIServlet ,這是用于部署的標準Servlet類。 對于此應用程序,無需修改。
組件的創建
如上所述,我們所有的組件都將在init()聲明。 建議不要將其作為最佳實踐,但對于范圍較小的應用程序來說效果很好。 雖然,當我們選擇一個新部門時,我們希望通過一個單獨的方法來集體更新大多數組件,這意味著這些組件將在此過程中被聲明為實例變量。
應用標題
我們從為標題創建標簽開始。 由于其值不會改變,因此可以在本地聲明。
Label appTitle = new Label("Employee Application"); appTitle.setStyleName("h2");除了一個值,我們給它一個樣式名稱。 樣式名稱允許完全控制組件的外觀。 在這種情況下,我們使用內置的Vaadin Valo主題,只需將參數設置為“ h2”即可選擇標題樣式。 此樣式名稱還可以用于使用自定義CSS定位組件(例如.h2 {font-family:'Times New Roman;})。
文字欄位
要查看所選部門的員工人數和平均工資,我們使用TextField組件。 TextField主要用于用戶文本輸入,盡管通過將其設置為只讀,我們禁止任何用戶交互。 請注意如何用空格分隔兩個樣式名稱。
noOfEmployees = new TextField("Number of employees"); // Instance variable noOfEmployees.setReadOnly(true); // Multiple style names are separated with a blank space noOfEmployees.setStyleName("huge borderless");盡管具有不同的標題和變量名稱,但該代碼對于averageSalary TextField是重復的。
圖表
可以使用Vaadin圖表附件輕松創建圖表,并且像其他任何組件一樣,使用具有相應屬性的圖表Java Object也可以創建圖表。 對于此應用程序,我們使用了COLUMN圖表來查看性別平衡,并使用AREASPLINE進行工資分配。
/* Column chart to view balance between female and male employees at a certain department */ genderChart = new Chart(ChartType.COLUMN); Configuration genderChartConfig = genderChart.getConfiguration(); genderChartConfig.setTitle("Gender Balance");// 0 is only used as an init value, chart is populated with data in updateUI() maleCount = new ListSeries("Male", 0); femaleCount = new ListSeries("Female", 0); genderChartConfig.setSeries(maleCount, femaleCount);XAxis x1 = new XAxis(); x1.setCategories("Gender"); genderChartConfig.addxAxis(x1);YAxis y1 = new YAxis(); y1.setTitle("Number of employees"); genderChartConfig.addyAxis(y1);與圖表關聯的大多數屬性均由其配置控制,該配置可通過getConfiguration()檢索。 然后用于添加圖表標題,兩個數據系列和軸屬性。 對于genderChart ,由于其簡單的性質,使用了一個簡單的ListSeries來保存數據。 盡管對于salaryChart下面,一個DataSeries選擇,因為它處理一個更大,更復雜的數據集。
該聲明salaryChart非常類似于的genderChart 。 同樣,檢索配置并將其用于添加標題和軸。
salaryChart = new Chart(ChartType.AREASPLINE);由于兩個圖表都顯示了男性和女性的數據,因此我們決定使用一個固定的傳說,該傳說我們固定在salaryChart 。
/* Legend settings */ Legend legend = salaryChartConfig.getLegend(); legend.setLayout(LayoutDirection.VERTICAL); legend.setAlign(HorizontalAlign.RIGHT); legend.setVerticalAlign(VerticalAlign.TOP); legend.setX(-50); legend.setY(50); legend.setFloating(true);最后,我們添加兩個空的DataSeries ,稍后將用數據填充該數據系列。
// Instance variables to allow update from UpdateUI() maleSalaryData = new DataSeries("Male"); femaleSalaryData = new DataSeries("Female"); salaryChartConfig.setSeries(maleSalaryData, femaleSalaryData);部門選擇器
最后一塊是部門選擇器,它控制應用程序的其余部分。
/* Native Select component to enable selection of Department */ NativeSelect<Departments> selectDepartment = new NativeSelect<>("Select department"); selectDepartment.setItems(DataModel.departments()); selectDepartment.setItemCaptionGenerator(Departments::getDeptName); selectDepartment.setEmptySelectionAllowed(false);
我們將其實現為NativeSelect <T>組件,該組件調用以前在DataModel定義的departments() ,以從數據庫中檢索部門流。 接下來,我們指定要在下拉列表中顯示的Department屬性(默認為toString() )。
由于不允許空選擇,因此將defaultDept設置為Department Stream的第一個元素。 請注意, defaultDept存儲為變量,以備后用。
/* Default department to use when starting application */ final Departments defaultDept = DataModel.departments().findFirst().orElseThrow(NoSuchElementException::new); selectDepartment.setSelectedItem(defaultDept);將組件添加到UI
到目前為止,我們只聲明了這些組件,而沒有將它們添加到實際的畫布中。 要在應用程序中顯示它們,它們都需要添加到UI中。 這通常是通過將它們附加到Layout來完成的。 布局用于創建結構化的層次結構,并且可以嵌套在一起。
HorizontalLayout contents = new HorizontalLayout(); contents.setSizeFull();VerticalLayout menu = new VerticalLayout(); menu.setWidth(350, Unit.PIXELS);VerticalLayout body = new VerticalLayout(); body.setSizeFull();如上面的代碼所示,為此使用了三種布局,一種是水平布局,兩種是垂直布局。 一旦定義了布局,我們就可以添加組件。
menu.addComponents(appTitle, selectDepartment, noOfEmployees, averageSalary); body.addComponents(genderChart, salaryChart); contents.addComponent(menu); // Body fills the area to the right of the menu contents.addComponentsAndExpand(body); // Adds contents to the UI setContent(contents);組件按添加順序出現在UI中。 對于VerticalLayout例如菜單),這意味著從上到下。 請注意, HorizontalLayout內容如何保持兩個VerticalLayout放置。 這是必要的,因為UI本身只能容納一個組件,即將所有組件作為一個單元保存的內容。
在用戶界面中反映DataModel
現在所有視覺效果都就位了,是時候讓它們反映數據庫內容了。 這意味著我們需要通過從DataModel檢索信息來向組件添加值。 我們的數據模型和EmployeeUI之間的橋接將通過處理來自selectDepartment事件來selectDepartment 。 這是通過在init()添加選擇偵聽器來完成的:
selectDepartment.addSelectionListener(e ->updateUI(e.getSelectedItem().orElseThrow()) );由于尚未定義updateUI() ,因此這是我們的下一個任務。
private void updateUI(Departments dept) { }這是我們要updateUI()完成的快速提醒:選擇新部門后,我們要計算和顯示員工總數,男性和女性人數,總平均薪水以及男性的薪水分布和那個部門的女性。
方便DataModel ,我們在設計數據模型時就考慮到了這一點,可以輕松地從數據庫中收集信息。
我們從文本字段的值開始:
final Map<Employees.Gender, Long> counts = DataModel.countEmployees(dept);noOfEmployees.setValue(String.format("%,d", counts.values().stream().mapToLong(l -> l).sum()));averageSalary.setValue(String.format("$%,d", DataModel.averageSalary(dept).intValue()));男性和女性的總和給出了雇員總數。 averageSalary()返回轉換為int的Double 。 這兩個值在傳遞到文本字段之前都被格式化為String 。
我們還可以通過檢索男性和女性的單獨計數,使用Map計數來填充第一張圖。
final List<DataSeriesItem> maleSalaries = new ArrayList<>(); final List<DataSeriesItem> femaleSalaries = new ArrayList<>();DataModel.freqAggregation(dept).streamAndClose().forEach(agg -> {(agg.getGender() == Gender.F ? femaleSalaries : maleSalaries).add(new DataSeriesItem(agg.getInterval() * 1_000, agg.getFrequency()));});我們DataModel提供了一個Aggregation ,我們可以認為含有性別,工資的元組的列表和相應的工資頻率(有多少人分享的薪水)。 通過在Aggregation流傳輸,我們可以將包含男性和女性數據的兩個List包含DataSeriesItem 。 在這種情況下, DataSeriesItem使用類似于具有x和y值的點。
Comparator<DataSeriesItem> comparator = Comparator.comparingDouble((DataSeriesItem dsi) -> dsi.getX().doubleValue());maleSalaries.sort(comparator); femaleSalaries.sort(comparator);在將數據添加到圖表之前,我們按x值的升序對其進行排序,否則,該圖表將看起來非常混亂。 現在,我們的兩個排序List<DataSeriesItem>會非常適合與DataSeries salaryChart的。
//Updates salaryChart maleSalaryData.setData(maleSalaries); femaleSalaryData.setData(femaleSalaries); salaryChart.drawChart();由于我們要更改整個數據集而不是單個點,因此我們將DataSeries的數據設置為剛創建的x和y的列表。 與ListSeries的更改不同,這不會觸發圖表的更新,這意味著我們必須使用drawChart()強制進行手動更新。
最后,在應用程序啟動時,我們需要使用默認值填充組件。 現在,可以通過在init()末尾調用updateUI(defaultDept)來完成此操作。
Java樣式
Vaadin在為組件增加個人感覺方面提供了完全的自由。 由于這是純Java應用程序,因此僅使用了Java框架中可用的樣式選項,盡管CSS樣式自然可以完全控制視覺效果。
應用ChartTheme之前和之后的比較。
為了使圖表具有個人風格,我們創建了一個ChartTheme類, ChartTheme擴展了Theme 。 在構造函數中,我們定義了要更改的屬性,即數據系列的顏色,背景,圖例和文本。
public class ChartTheme extends Theme {public ChartTheme() {Color[] colors = new Color[2];colors[0] = new SolidColor("#5abf95"); // Light greencolors[1] = new SolidColor("#fce390"); // YellowsetColors(colors);getChart().setBackgroundColor(new SolidColor("#3C474C"));getLegend().setBackgroundColor(new SolidColor("#ffffff"));Style textStyle = new Style();textStyle.setColor(new SolidColor("#ffffff")); // White textsetTitle(textStyle);} }然后通過將這一行添加到init() ,將主題應用于所有圖表:
ChartOptions.get().setTheme(new ChartTheme());結論
我們使用Speedment來連接數據庫,使用Vaadin來連接最終用戶。 兩者之間唯一需要的代碼就是幾個Java Streams構造,以聲明方式描述應用程序邏輯,從而使上市時間和維護成本降至最低。
可以從GitHub上分叉此倉庫,并開始自己嘗試。
s
Julia·古斯塔夫森(Julia Gustafsson)
Per Minborg
翻譯自: https://www.javacodegeeks.com/2018/06/full-stack-java.html
snmp與java集成
總結
以上是生活随笔為你收集整理的snmp与java集成_轻松地与Java完全集成的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小米手机的设置技巧有什么
- 下一篇: gwt格式_活性GWT