發表文章

目前顯示的是 2021的文章

[C#] 拖拉 Windows Form 的 Component

圖片
最近有需要想將 Windows Form 上的元件(例如 Label、Button)隨使用者自由拖拉,雖然事件都有 Drag 相關的可以使用,但查了一下似乎不是我想要的,這些 DragDrop()、DrapOver() 事件還有 AllowDrop 屬性好像是拖拉資料,並不是拖拉元件,只好手動用 MouseDown()、MouseMove()、MouseUp() 事件來組合。首先如下兩圖,先設置了一個 Label 叫 label1,有設定背景色和 AutoSize = false,左圖是本來的位置,右圖是用滑鼠左鍵拖拉後的結果: 這些滑鼠事件的參數 MouseEventArgs e 有 X 和 Y 兩個屬性,是 滑鼠相對於元件的坐標 ,不是相對整個表單,也不是相對整個螢幕,例如: 其實相對於什麼倒也不是這麼重要,只要得到位移向量,就能找出後來元件新的位置為何,如下圖,首先要兩個點:P 為最開始點擊 MouseDown() 事件時的定點,而 Q 為 MouseMove() 過程任何時候的動點,然後用PQ連出的向量 t,加上原來物件相對於整個表單的坐標向量 u,就得到物件新的位置的坐標向量 v: P 點就是 MouseMove() 事件中要先取到的 clickPoint,而 Q 點是 MouseMove() 事件中不停變動的 ( e.X , e.Y ),向量 t 為 ( e.X - clickPoint.X , e.Y - clickPoint.Y ),這個向量 t 再加上之前物件 Location 就得新的 Location ,也就是 ( Location.X + e.X - clickPoint.X , Location.Y + e.Y - clickPoint.Y ),參考程式碼如下: namespace DragComponentDemo { public partial class Form1 : Form { bool isDraging = false; Point clickPoint; public Form1() { InitializeComponent(); } private void label1_MouseDown(object sender, ...

[C#] 產生相異亂數

圖片
C# 有 Random 物件和 .Next() 等方法產生亂數,但不像 Python 有 random.sample() 等可直接產生相異隨機數字的好用方法,就整理了一下幾個並比較效率。以下目標都是產生 start 到 end 之間(頭尾有包含),number 個的相異且隨機的整數,並成一個整數陣列。例如 start = 20 、end = 50、number = 10,會產生 {37, 25, 48, 20, 32, 50, 44, 42, 29, 46} 作法一:單純 for loop 利用 Array 的 .Contains() 方法,若產生的新數已經出現過,就將 index-- 來重取,直到迴圈結束: public int[] RandomDistinctByForLoop(int start, int end, int number) { Random rnd = new Random(); int[] arrayResult = new int[number]; for (int i = 0; i < number; i++) { int rndResult = rnd.Next(start, end + 1); if (arrayResult.Contains(rndResult)) // 若已存在 { i--; // 使 for 不會步進,再重覆一次 } else // 若不存在 { arrayResult[i] = rndResult; // 存入亂數結果 } } return arrayResult; } 簡單好寫,缺點是如果 number 很大時,在後期會因為多數都已重覆所以耗費大量時間。 作法二:單純 while loop 和上面差不多,用 while 來思考,很容易想到的寫法,若產生的新數沒有出現過,就放入 Array,並將 index++ ,直到數量達成`: public int[] RandomDistinctByWhileLoop(int start, int...

[C#] 在code中新增物件和事件

圖片
雖然 Windows Form 很方便要什麼元件,手動拉一拉就好,但還是有機會要從 code 去新增,像下面是作業要做的樂透彩的產生器和對獎器,那麼多個數字要一個個拉動,還要設定 MouseClick 事件,非常辛苦,就想用 for loop 去產生: 大概要注意的點是要讓 Form.Controls.Add() 新增產生出來的物件給 Form,我這裡用的是 Label 和其上的 MouseClick 事件,這裡要用上 delegate,還要在滑鼠按下左鍵時做出背景與前景顏色改變,那就會牽涉到是哪個物件被按,最後是如果需要所在位置 i 的值,來與其他資料作運用,為避免界限例外的一個處理。 namespace TWLotteryExercise { public partial class Form1 : Form { Label[] lblNumber = new Label[8]; bool[] isSelected = new bool[8]; public Form1() { for (int i = 0; i < 8; i++) { isSelected[i] = false; // 用來判斷 Label 是否按下用的陣列 } for (int i = 0; i < 8; i++) { lblNumber[i] = new Label(); // Label 的設定還有 Location lblNumber[i].Text = $"{i+1}"; // Width Height 等等不贅述 lblNumber[i].BackColor = Color.White; // 最初是白底黑字 lblNumber[i].ForeColor = Color.Black; int idx = i; // 若以下事件的方法中用 i 會報錯,另外令一個 idx 就過了 lblNumber[i].MouseClick += new Mo...

[C#] 分割圖片

圖片
練習製作滑塊拼圖小遊戲,必須用到圖片切割,所以去找了一下其實還蠻簡單的,System.Drawing.Bitmap 就有方法了,此知識來自 StackOverFlow : How to crop an image using C#? 。 using System.IO; try { Image img = Image.FromFile(@"C:\.....\sample.jpg"); Bitmap bmp = (Bitmap)img; } catch(FileNotFoundException ex) { } Rectangle rect = new Rectangle(100, 180, 300, 200); Image result = bmp.Clone(rect, img.PixelFormat); 上面的程式碼就會將 sample.jpg 圖片上 (100, 180) 的位置開始切下寬300、高200的長方形賦予給 result,下為示意圖片: 就會得到黃色矩形的圖片,如下 要如影片切割成多塊來移動,我是用 PictureBox[] ,要注意的是第幾個 row 其實在坐標上有關的是 y 坐標,而第幾個 column 對應的是 x 坐標,另外因為 PictureBox 是另外 new 出來不是手動滑鼠拉的,必須有 Form.Controls.Add() 來讓他出現在視窗上。 using System.IO; PictureBox[] picBlock = new PictureBox[16]; int blockWidth, blockHeight; try { Image img = Image.FromFile(@"C:\.....\sample.jpg"); Bitmap bmp = (Bitmap)img; blockWidth = img.Width / 4; blockHeight = img.Height / 4; } catch(FileNotFoundException ex) { } for(int i=0; i

[C#] GUI 中的 .RotateTransform() 方法無旋轉中心參數

圖片
在用 C# 練習時鐘問題,要將圖片旋轉時發現 Graphics.RotateTransform() 方法竟然只有一個參數就是角度(另一個多載也沒有),如上圖上方是 C#,下方是 Java,促使我研究了一下,其實不難。首先先瞭解一下電腦中的坐標系是向右為 x 軸正向,向下是 y 軸正向,如下圖: 有摸 GUI 的人應該很熟悉了,其實四個象限存在,只是視窗可視的部份是 x,y 皆正,上圖我用黃色區域表示第一象限。如果要把一張圖片(這裡我用台灣翠青旗)畫出來,要求旗子的左上角定位在 (20, 30) P 這一點,如下圖: 這非常簡單,C# 的話用 DrawImage() 方法: namespace RotateDemo { public partial class Form1 : Form { Image flag; public Form1() { try { flag = Image.FromFile("flag.png"); } catch(Exception) { } } private void Form1_Paint(object sender, PaintEventArgs e) { Graphics gfx = e.Graphics; gfx.DrawImage(flag, 20, 30); // 重點在這行 } } } 接下來我都省略其他部份著重在 gfx 的動作,如果要將形狀為矩形的旗子正中央定位在 (20, 30),則 DrawImage 坐標的引數減去矩形寬與高的一半即可: gfx.DrawImage(flag, 20-38/2, 30-26/2); // 假定圖片(旗子)大小是寬38,高26,不精準的示意尺寸 以上兩個可以利用 Graphics 的 .TranslateTransform() 平移變換方法。想像一下 Graphics 有一張自己的畫布,和螢幕上可視的視窗不一樣,最開始原點 O' 和視窗上的原點 O 重合,兩軸稱作 u 軸和 v 軸,分別與視窗上的 x 軸和 y 軸重...

矩形與矩形碰撞(皆旋轉矩形)

圖片
碰撞主題的最後,是凸多邊形碰撞的解法應用在矩形上,想法來自 Stack Overflow : How to check intersection between 2 rotated rectangles? 的回應 ,正如標題描述要處理的是,兩個有旋轉的矩形之碰撞檢定。 以四邊形 ABCD 和三角形 EFG 為例,上圖是邊界有相交,下圖是沒有,當兩凸多邊形未碰撞時,中間會有空隙一定找的到一條直線 L 使得兩個圖形各在直線的異側,如下圖: 顯然這樣的直線不是唯一,不只可以將直線平移,其至稍微旋轉一點也可以,在兩凸多邊形邊界未相交時,可以隔開兩個圖形的直線有無限多條,如下圖有 L、M、N 三條: 換句話說只要找出一條直線,可使得兩個圖形各落在直線的一側,就確定兩圖形沒有碰撞,那麼這樣的直線怎麼找呢?再觀察上圖,顯然隔開的直線可以取「平行多邊形的一邊的直線」,例如上圖的直線 L 就平行線段 FG,而直線 M 和直線 N 就平行線段 AB,其實也不是一定要平行多邊形的邊,例如上圖中斜率介於 L 和 M 之間的直線也能夠將兩圖形分開,只是我們從平行多邊形一邊的直線來著手。 再回來看四邊形 ABCD 在左邊,三角形 EFG 在右邊,被直線 L 隔開的圖: 如果兩凸多邊形被直線 L 分開,應該有什麼性質呢?我們考慮將兩個圖形沿著直線 L 的方向投影出去,也就是取和直線 L 垂直的直線 N ,將四邊形 ABCD 與三角形 EFG 都投影到直線 N 上,各自會得到線段,如下圖: 顯然投影的兩個線段不會相交,四邊形 ABCD 在直線 N 上的正射影是線段 A'C',而三角形 EFG 在直線 N 上的正射影是線段 F'G',兩個線段並沒有重疊,上圖是取「平行線段 CD 的直線 L 和垂直的直線 N」,如果看另外一組: 上圖是取「平行線段 EG 的直線 L 和垂直的直線 N」,同樣的將四邊形 ABCD 和三角形 EFG 投影到直線 N 上的結果,顯然洋紅色與青綠色兩個投影線段這次就有重疊了,兩個多邊形在「邊 EG 的法向量」上的正射影有重疊,表示我們無法用平行 EG 的直線將兩個圖形分開在兩邊,但這不代表兩個圖形就有碰撞,只是代表不能用平行線段 EG 的直線去隔開兩個圖形。可能用別的邊和法向量看投影,就可...

矩形與矩形碰撞(未旋轉)

圖片
這應該是旋轉主題裡面最簡單的吧,如果是用 Java Swing 矩形為框的眾多 JComponent 像 JLabel JButton 就有 .getBounds() 得到 Rectangle 物件,並有 .intersects() 方法可用。不過這個碰撞檢定剛好可視為旋轉矩形碰撞檢定的特例,我個人認為可以比較一下,所以未旋轉兩矩形的碰撞檢定還是做了個筆記。 未旋轉的擺正矩形,四個邊會平行兩軸,我們考慮矩形投影到 x 軸,會得到線段,如果兩個矩形有碰撞,則兩個投影線段會相交,相反地矩形沒有碰撞的話,投影到 x 軸的線段就會分開,我們可用線段的最左最右來比較是否重疊,可知其實就是比較原來矩形的左邊 x 坐標和右邊 x 坐標,如下圖: 如上圖,未碰撞可分兩種情形,一個是矩形 A 的最右邊小於矩形 B 的最左邊,也就是整個矩形落在另一個矩形的左側,不管矩形 A 上下如何移動,都和矩形 B 不會相交;同理當矩形 A 的最左 x 坐標大於矩形 B 的最右 x 坐標,兩矩形不會碰撞。 以此類推還有上下的: 如上圖,當其中一個矩形的上緣比另一個矩形的下緣還要低,則不會相交;當其中一個矩形的下緣比另一個的上緣還要更高,也不會相交。 參考 Java Code: public boolean isRectRectCollide(Rectangle rectA, Rectangle rectB) { if(rectA.Left() > rectB.Right() || rectA.Right() < rectB.Left()) { return false; // Rectangle.Left() 取得矩形左邊線段的 x 坐標 } if(rectA.Top() > rectB.Bottom() || rectA.Bottom() < rectB.Top()) { return false; // Rectangle.Top() 取得矩形上方線段的 y 坐標 } // 要注意電腦坐標系是 y 是以下為正 return true; } 圓形與圓形碰撞 圓形與矩形碰撞(無旋轉矩形) ...

圓形與矩形碰撞(有旋轉矩形)

圖片
承上篇,如果是寬高與兩軸不平行,也就是經過旋轉的矩形,如何檢定他和圓形的碰撞?如下圖: 因為上一篇已經完成了圓形與擺正的矩形之碰撞檢定,那就可以將不平行的矩形轉正,利用已完成的函式來解決這個問題。首先要將矩形轉正,就需要找到角度,能夠直接從物件中得到旋轉角度是最好的(我自己設計遊戲物件就有做旋轉角度的 getter setter),沒有的話可能要考慮多寫一個方法。 如上圖,找到矩形從擺正到現在傾斜是轉了θ度的話,接下來考慮將圓形對矩形的中心旋轉 ( - θ ) 度。為什麼要這麼做呢? 其實是將圓形與矩形兩個都對矩形中心旋轉 ( - θ ) 度。如此一來兩個圖形就符合上一篇的前提,這樣來判斷相交情形,就代表原來的兩圖形是否碰撞的結果是一致的,因為對同一個旋轉中心旋轉同樣的角度,之前之後的兩圖形相對位置並沒有改變。再加上矩形本身對自己的中心旋轉,其寬的一半、高的一半和中心坐標,還是一樣,所以只需要考慮圓形對矩形中心旋轉後的結果,又半徑也不受旋轉影響,就剩會影響「兩中心點水平差」和「兩中心點鉛直差」的新圓心坐標了,所以結論是: 目標是找出圓心對矩形中心旋轉 ( - θ ) 後的新坐標 將圓形與矩形對矩形中心旋轉 ( - θ ) 得到的結果: 有旋轉得新坐標的方法是最好,不難也可以再重寫,步驟大概有: 先平移 ( - 矩形中心 ),即平移後坐標 = 圓心坐標 - 矩形中心坐標, 乘上 ( - θ ) 的旋轉矩陣 [ [cos(-θ) , - sin(-θ)] , [ sin(-θ) , cos(-θ) ] ] 再平移矩形中心坐標,坐標加上 矩形中心坐標 得圓形旋轉後新坐標,就可以引入前一篇的函式: public boolean isCircRotateRectCollide(Circle circ, Rectangle rect) { double theta = Math.toRadians(rect.Angle() * (-1)); // 旋轉負的角度 Circle rotatedCirc = new Circle(); int transX = circ.X() - rect.X(); // 平移 int transY = circ.Y(...

圓形與矩形碰撞(無旋轉矩形)

圖片
承上一篇,接下來是檢定圓形和矩形的碰撞,想法來自這篇 StackOverflow : Circle-Rectangle collision detection (intersection) ,不過這個只能檢定擺正沒有經過旋轉的矩形,即邊長平行兩軸的矩形,例如: 檢定分成三個部份,由於圓形只會靠近一個角落,所以將矩形切割成四個部份,只討論一個角落,碰撞檢定是全部一起看的,只是接下來圖片的呈現只用右上角為例。 第一部份,最先排除足夠遠的可能 如上圖,當圓形夠遠的情形先排除掉,例如圖中紅色的區域,當圓形的圓心在紅色區域時,兩圖形必不相交,那判斷的依據是什麼呢? 當圓形的圓心和矩形的中心(對角線交點)的水平距離超過矩形寬度的一半加上圓半徑,圓形和矩形就足夠遠不會相交,同理圓心和矩形中心的鉛直距離超過矩形高度的一半加上圓半徑,也不會相交,這時候圓心就會落在紅色區域,兩圖形就不會碰撞。此處要用的數據有: 兩中心的水平差 = |圓心 x 坐標 - 矩形中心 x 坐標| 兩中心的鉛直差 = |圓心 y 坐標 - 矩形中心 y 坐標| 矩形寬度的一半 + 圓半徑 矩形高度的一半 + 圓半徑 第二部份,討論餘下之中,足夠近的情形。 經過第一部份,剩下的兩圖形之中心的可能,會被限制在「水平差 <= 矩形寬的一半 + 圓半徑」,而且「鉛直差 <= 矩形高的一半 + 圓半徑」,此時圓心會落在上圖的藍色與黃色區域內,如上圖。 在此情形下如果又更靠近,例如「鉛直差 <= 矩形高的一半」,又因為「水平差 <= 矩形寬的一半 + 圓半徑」則圓心落在黃色區域中,兩圖形就相交;同理如果「水平差 <= 矩形寬的一半」,又因為「鉛直差 <= 矩形高的一半 + 圓半徑」則圓心也落在黃色區域中,兩圖形相交。 要用的數據有: 兩中心的水平差 = |圓心 x 坐標 - 矩形中心 x 坐標| 兩中心的鉛直差 = |圓心 y 坐標 - 矩形中心 y 坐標| 矩形寬度的一半 矩形高度的一半 第三部份,細膩判斷角落的情形。 最後,就剩下在第二部份的圖片中藍色的正方形,這是以圓半徑圍成的正方形。圓心落在此處的圓可能與矩形相交,也可能不會: 如果圓形與矩形要有交點,考慮...

圓形與圓形碰撞

圖片
做遊戲的過程一定會碰到邊界碰撞的檢定,邊界較常見的就圓形和矩形,下面記錄一下,就先最簡單的圓形與圓形的碰撞,原理就是初中三年級的圓形幾何,當兩圓外切時連心線恰為兩半徑之和,當兩圓交於兩點時,兩半徑與連心線會形成三角形,故連心線 <= 半徑之和,則兩圓相交: 其實稱呼這個要求為疊合會更恰當,像是兩圓之位置關係為內離不能算是碰撞,因為邊界不相交,但是遊戲上多是從外部來靠近,即使邊界相交甚至到其一個物件全部落在另一個物件之中,碰撞已經發生了,所以不太去排除完全落在內部的情形,以下是 Java 程式碼: public boolean isCircCircCollide(Circle circA, Circle circB) { double distance = Math.sqrt( // 計算連心線長 Math.pow(circA.X() - circB.X(), 2) // Circle.X() 得圓心x坐標 + Math.pow(circA.Y() - circB.Y(), 2) ); // Circle.Radius() 得半徑 return circA.Radius() + circB.Radius() >= distance; } 圓形與圓形碰撞 圓形與矩形碰撞(無旋轉矩形) 圓形與矩形碰撞(有旋轉矩形) 矩形與矩形碰撞(未旋轉) 矩形與矩形碰撞(皆旋轉矩形)

[Java] JFrame JPanel 設背景圖片

在 JFrame 和 JPanel 中雖然有背景的設定 .setBackground() 但參數只能是顏色 Color.COLORNAME 或 new Color(r,g,b)。如果背景要放圖片,可能會想到這樣: import java.awt.Dimension; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class JFrameBackgroundDemo extends JFrame{ JFrameBackgroundDemo(){ JPanel myPanel = new JPanel(); // 用 Dimension 和 JFrame 的 .pack() 可保證大小正確 myPanel.setPreferredSize(new Dimension(800,600)); JLabel bgLabel = new JLabel(); bgLabel.setIcon(new ImageIcon("img/background.png")); bgLabel.setLocation(0, 0); bgLabel.setVisible(true); myPanel.add(bgLabel); this.add(myPanel); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); this.pack(); } } 背景圖片路徑是 img/ 下的 background.png。想法是用一個 JLabel 使用該圖片然後定位在 (0, 0) 但這不是好辦法,因為他會推開其他 JComponent 而且當其他有動作時會被 JLabel 蓋住(我對 javax 的上下層邏輯實在無法理解),所以改用別的辦法,參考自 StackOverflow : Simplest way to set image as JPanel backgro...

[Java] 用 InputMap ActionMap KeyStroke 並解決按鍵遲鈍問題

這篇等於是結合前面兩篇 利用 getInputMap、getActionMap 和 KeyStroke 從外部新增 Action 和 javax 中 KeyListener 的遲鈍問題 的結果 概念上是用 KeyStroke.getKeyStroke("pressed RIGHT") 得到按下右鍵時,讓 InputMap 對應到 "rightAct" 字串,再讓 ActionMap 對應到一個動作,而這個動作物件是在外部再描述,讓 myLabel 裡的物件成員 ArrayList<Character> pressedKey 來增減內容,當沒有 'r' 時增加 'r' 進去,而且若是第一次按鍵就啓動 timer;而 KeyStroke.getKeyStroke("released RIGHT") 放開右鍵時,對應到 "rightCancel" 字串,由 ActionMap 對應到動件,在外描描述此動作,當 pressKey 之中有 'r' 時刪去,如果完全沒有按鍵時則 timer 停止。timer 會讓 update() 不停的執行,而 update() 內容也由外部覆寫,處理目前按下了左鍵還是右鍵,讓 myLabel 更新他的位置來左右移動。 GameObject.java package game; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.JLabel; import javax.swing.Timer; public class GameObject extends JLabel implements ActionListener{ private int delay; private Timer timer; private ArrayList<Character> pressedKeyList = new ArrayList<>(); public GameOb...

[Java] 利用 getInputMap、getActionMap 和 KeyStroke 從外部新增 Action

承自上一篇 javax 中 KeyListener 的遲鈍問題 解決了聽取按鍵的延遲問題,終於看懂那個 StackOverflow 的解答,試著自己把他做出來,而且更貪心地我想做成一個類別,之後都能用,就像 pygame 裡 sprite 的 update() 一樣。但是 Java 寫起來超級長而且很不自由,非常不滿意,期待之後能改良: 首先這個類別 GameObject 是繼承自 JLabel ,他是 JComponent 的子類別就有 InputMap 和 ActionMap 可以使用鍵盤功能,簡單型式(不考慮聽取鍵盤的延遲問題的話的作法)如下: GameObject.java package game; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JLabel; import javax.swing.KeyStroke; public class GameObject extends JLabel implements ActionListener{ Action goLeft; // 宣告一個 Action 準備給按下左鍵時動作,內容在下面重載 public GameObject() { goLeft = new LeftAction(); this.getInputMap().put(KeyStroke.getKeyStroke("pressed LEFT"), "leftAct"); this.getActionMap().put("leftAct", goLeft); // 用 "pressed Left" 字串會得到一個鍵值,並對應到 "leftAct" 字串 // 再用 Action...

[Java] javax 中 KeyListener 的遲鈍問題

看 GrandmaCan -我阿嬤都會(小白) 的3小時用 Pygame 做出遊戲 影片,想說自己來用 Java 做做看,在移動太空船的地方,想法大概是用 JLabel 來做太空船,在 JFrame 用 .addKeyListener(new KeyListener(){ }) 來取得鍵盤輸入,當輸入 'a' 時( code 65)將太空船的位置 x 減少就會左移,輸入 'd' 時(code 68)將 x 增加就會右移,程式碼大概如下: 基本的 KeyListener 聽取鍵盤輸入 mainStart.java public class mainStart { public static void main(String[] args) { new TestBasicKeyListener(); } } TestBasicKeyListener.java import java.awt.Color; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class TestBasicKeyListener extends JFrame{ JPanel myPanel; JLabel myLabel; public TestBasicKeyListener() { myPanel = new JPanel(); myLabel = new JLabel(); myPanel.setSize(500,600); myLabel.setSize(50,40); //設定一個長50寬40的JLabel myLabel.setBackground(Color.orange); myLabel.setOpaque(true); //背景橘色,不透明 myPanel.setLayout(null); myPanel.add(myLabel); myPan...

[Java] Hibernate CRUD

一切設定好後(有 使用 hbm.xml 對應檔案 ,和 持久化物件中使用 Annotation 兩種) Hibernate 中使用 CRUD (Create、Read、Update、Delete) import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import java.util.List; public class TestIO { private static SessionFactory factory; public static void main(String[] args) { factory = new Configuration() .configure() .buildSessionFactory(); //若使用 Annotation 則在 buildSessionFactory()之前要 .addAnnotatedClass(Obj.class) Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); // Create Obj myObj1 = new Obj("data2", data3); session.save(myObj1); // 由建構子設定 myObj1 兩個值(id 由 Hibernate 決定),執行session.save() 會 // 準備將之依照對應 column 各值存入資料庫表格中 // Read 用主鍵取特定資料 Obj myObj2 = (Obj)session.get(Obj.class, 5); System.ou...

[Java] Hibernate 使用 Annotation 配置

Hibernate 安裝 好之後,若用物件內定義註釋 Annotation 的作法如下: 檔案配置 在 src / main / java 右鍵 New > Other... 搜尋 hibernate,選擇 Hibernate Configuration File (cfg.xml),點 Next 使用預設的檔名 hibernate.cfg.xml 即可(若更動檔名則後續在使用時, .configure() 要另外指定檔名) 可用他的 Wizard 來輸入完成 cfg.xml ,或直接 Finish ,在檔案右鍵 Open With > Generic Text Editor 手動輸入 如果 MySQL 的 database 叫 mydb,帳號 root,密碼 abc1234,則 hibernate.cfg.xml 範例如下: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mydb</property> <property name="hibernate.connection.username...

[Java] Hibernate 使用 .hbm.xml 配置

Hibernate 安裝 好之後,若用對應檔案 .hbm.xml 的作法如下: 檔案配置 在 src / main / java 右鍵 New > Other... 搜尋 hibernate,選擇 Hibernate Configuration File (cfg.xml),點 Next 使用預設的檔名 hibernate.cfg.xml 即可(若更動檔名則後續在使用時, .configure() 要另外指定檔名) 可用他的 Wizard 來輸入完成 cfg.xml ,或直接 Finish ,在檔案右鍵 Open With > Generic Text Editor 手動輸入 如果 MySQL 的 database 叫 mydb,帳號 root,密碼 abc1234,則 hibernate.cfg.xml 範例如下: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mydb</property> <property name="hibernate.connection.username"...

[Java] Hibernate 基本配置

下載安裝 Hibernate google "Hibernate Tools" 或 "JBoss Tools" 進入 JBoss Tools 選 Download ,找穩定版本(例如 JBoss Tools 4.19.1.Final) 點 Download,獲得一個 URL (例如 https://download.jboss.org/jbosstools/photon/stable/updates/) 打開 Eclipse JavaEE,選擇 Help > Install new Software... > 在 Work with 填入上述 URL 靜候片刻,清單會列出很多可以下載的東西,在 Filter 輸入 hibernate ,點選 Hibernate Tools 和 JBoss Maven Hibernate Configurator,點選 Next 後會開始下載,進度條在右下角要注意不要沒下載完就關閉 依提示同意條款和重開 Eclipse 後,File > New > Other... 在 Wizard 中搜尋 hibernate 若有 cfg.xml 和 hbm.xml 等四個選項則成功 google "Hibernate ORM" 進入 Hibernate.org 點選 ORM 標籤,選擇最新且穩定版本,下拉選 Zip Archive 下載 解開檔案(例如 hibernate-release-5.5.7.Final.zip),可以找個 Source 資料夾集中管理 Library 配置 新增 MySQL 驅動 jar 檔(例如 mysql-connector-java-8.0.26.jar)到 project/src/main/webapp/WEB-INF/lib 下 將 Hibernate ORM 解開的 /lib/required/ 下所有 jar 檔新增到 project/src/main/webapp/WEB-INF/lib 下 以上應該就會新增這些 jar 檔了,若還沒有就在 Eclipse Java EE 中 Project Explorer 樹狀圖在上述 WEB-INF/lib 右鍵 Sour...

[Java] JDBC 使用 MySQL 筆記

雖然學 Hibernate 後可能就不太會重寫 JDBC 這些繁雜的 code 還是記錄一下 如果 MySQL 的 database 叫 mydb,帳號root,密碼abc1234,對表格 mytable 做 insert 、update 和 select 動作: import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; public class JdbcSample { String driverName = "com.sql.cj.jdbc.Driver"; // Java6之前用com.sql.jdbc.Driver String connectionString = "jdbc:mysql://localhost:3306/mydb?" + "user=root&password=abc1234&useUnicode=true&characterEncoding=utf-8"; try { Class.forName(driverName); Connection conn = DriverManager.getConnection(connectionString); //獲得連線 Statement stat = conn.createStatement(); String insertString = "insert into mytable(col1, col2) values(data1, 'data2')"; int count1 = stat.executeUpdate(insertString); //對 MySQL 執行,增加一筆col1=data1、col2='data2'的資料到表格mytable中 //同時回傳一個整數給count表示此動作影響了幾個row String updateString =...

[Java] Eclipse 給 JDBC 增加 MySQL 的 driver

圖片
在 Eclipse Java SE 中輕鬆: 在 Package Explorer 中,在 project 上右鍵 選 Build Path ,選 Add External Archives... 找到 MySQL 資料夾中的 ./Connector J 8.0/ 的 mysql-connector-java-8.0.26.jar 確定 但在 Eclipse Java EE 就弄了很久,做個記錄: 在 Project Explorer 中,在 project 上右鍵 選 Build Path, 選 Configure Build Path... 切到 Source 標籤 選右邊 Link Source... Browse 找到 MySQL 放 mysql-connector-java-8.0.26.jar 檔案的資料夾 (./Connector J 8.0/ ) Apply 後回到 Project Explorer 會有 Connector J 8.0 打開會有 mysql-connector-java-8.0.26.jar 右鍵選 Build Path... 選 Add to Build Path 待增加了 Referenced Libraries 中有 mysql-connector-java-8.0.26.jar 更快更好的是把 jar 檔和專案放一起: 到 MySQL/Connector J 8.0/ 複製 mysql-connector-java-8.0.26.jar 一份到 project/src/main/webapp/WEB-INF/lib/ 在 Project Explorer 中樹狀圖找到上述 lib 資料夾,右鍵選 Source ,選 Format 展開 lib 就會看到 jar 檔出現在樹狀圖 lib 之下,再對 jar 檔右鍵 選 Build Path ,然後 Add to Build Path ,如此在 Referenced Libraries 中就有了

[Java] JDBC 使用 MySQL ResultSet

圖片
練習 JDBC 使用 MySQL,將 SELECT * FROM table; 的結果存成 ResultSet ,然後將他一個個 .next() 取出來每一 row 的數據,再getInt() 或 getString()。 我的練習是有三個 table 分別為 player、weapon_list 和 armor_list,在 player 中每一筆資料是角色,有名字有等級有職業有武器和防具,而武器防具只存放 id,要連去另外兩個 table weapon_list 和 armor_list 用的。練習 SELECT * FROM player; 將每個角色的資料印出來時,本來的打算是 getInt(weapon_id) 用這個去別的 table 再 query 一次取名字出來(或更進一步取攻擊力防禦力之類的),像這樣: Connection connection = DriverManager.getConnection("...."); Statement statement = connection.createStatement(); ResultSet result = statement.executeQuery("SELECT * FROM player"); while(result.next()) { ResultSet weaponQuery = statement.executeQuery("SELECT name FROM weapon_list WHERE id=" + result.getInt("weapon_id")); String weaponTemp = weaponQuery.getString("name"); ResultSet armorQuery = statement.executeQuery("SELECT name FROM armor_list WHERE id=" + result.getInt("armor_id")); String armorTemp = armorQuery.getString("name"); ...