מדריך לממשק JDBC ResultSet

1. סקירה כללית

ממשק ה- API של Java Database Connectivity (JDBC) מספק גישה למסד הנתונים מיישום Java. אנו יכולים להשתמש ב- JDBC כדי להתחבר לכל בסיס נתונים כל עוד מנהל ההתקן הנתמך של JDBC זמין.

ה ResultSet היא טבלת נתונים שנוצרת על ידי ביצוע שאילתות בסיס נתונים. במדריך זה, אנו נסתכל עמוק יותר על ה- ResultSet ממשק API.

2. יצירת א ResultSet

ראשית, אנו מאחזרים א ResultSet על ידי התקשרות לבצע שאילתה() על כל אובייקט שמיישם את הַצהָרָה מִמְשָׁק. שניהם הצהרה מוכנה וה CallableStatement הם ממשקי משנה של הַצהָרָה:

PreparedStatement pstmt = dbConnection.prepareStatement ("בחר * מעובדים"); ResultSet rs = pstmt.executeQuery ();

ה ResultSet האובייקט שומר על סמן המצביע על השורה הנוכחית של קבוצת התוצאות. נשתמש הַבָּא() על שלנו ResultSet לחזור דרך הרשומות.

הבא, אנחנו להשתמש ב getX () שיטות תוך כדי איטרציה בין התוצאות כדי להביא את הערכים מעמודי מסד הנתונים, איפה איקס הוא סוג הנתונים של העמודה. למעשה, אנו נספק שמות עמודות מסד נתונים ל- getX () שיטות:

בעוד (rs.next ()) {שם מחרוזת = rs.getString ("שם"); שלם empId = rs.getInt ("emp_id"); משכורת כפולה = rs.getDouble ("משכורת"); מיקום מחרוזת = rs.getString ("עמדה"); } 

כְּמוֹ כֵן, ניתן להשתמש במספר האינדקס של העמודה עם getX () שיטות במקום שם העמודה. מספר האינדקס הוא רצף העמודות במשפט SQL select.

אם המשפט שנבחר אינו מפרט שמות עמודות, מספר האינדקס הוא רצף העמודות בטבלה. מספור אינדקס העמודות מתחיל מאחד:

שלם empId = rs.getInt (1); שם מחרוזת = rs.getString (2); מחרוזת מיקום = rs.getString (3); משכורת כפולה = rs.getDouble (4); 

3. אחזור MetaData מה- ResultSet

בחלק זה, נראה כיצד לאחזר מידע על מאפייני העמודה וסוגי ה- a ResultSet.

ראשית, בואו נשתמש ב- getMetaData () שיטה על שלנו ResultSet להשיג את ResultSetMetaData:

ResultSetMetaData metaData = rs.getMetaData ();

לאחר מכן, בואו נקבל את מספר העמודות שיש בתוכנו ResultSet:

מספר שלם columnCount = metaData.getColumnCount ();

יתר על כן, אנו יכולים להשתמש בכל אחת מהשיטות הבאות באובייקט המטא-נתונים שלנו כדי לאחזר מאפיינים של כל עמודה:

  • getColumnName (int columnNumber) כדי לקבל את שם העמודה
  • getColumnLabel (int columnNumber) כדי לגשת לתווית העמודה, שצוינה לאחר מכן כפי ש בשאילתת SQL
  • getTableName (int columnNumber) כדי לקבל את שם הטבלה אליה שייך העמודה
  • getColumnClassName (int columnNumber) לרכישת סוג הנתונים של Java של העמודה
  • getColumnTypeName (int columnNumber) כדי לקבל את סוג הנתונים של העמודה במסד הנתונים
  • getColumnType (int columnNumber) כדי לקבל את סוג הנתונים SQL של ​​העמודה
  • isAutoIncrement (int columnNumber) מציין אם העמודה היא תוספת אוטומטית
  • isCaseSensitive (int columnNumber) מציין אם מקרה העמודה חשוב
  • ניתן לחיפוש (int columnNumber) מציע אם נוכל להשתמש בעמודה ב- איפה סעיף של שאילתת SQL
  • isCurrency (int columnNumber) מאותת אם העמודה מכילה ערך מזומן
  • isNullable (int columnNumber) החזרות אֶפֶס אם העמודה לא יכולה להיות אפסית, אחד אם העמודה יכולה להכיל ערך null, ו שתיים אם בטלות העמודה אינה ידועה
  • isSigned (int columnNumber) החזרות נָכוֹן אם הערכים בעמודה חתומים, אחרת מחזיר שֶׁקֶר

בוא נעבור דרך העמודות כדי לקבל את המאפיינים שלהם:

עבור (int columnNumber = 1; columnNumber <= columnCount; columnNumber ++) {String catalogName = metaData.getCatalogName (columnNumber); מחרוזת className = metaData.getColumnClassName (columnNumber); תווית מחרוזת = metaData.getColumnLabel (columnNumber); שם מחרוזת = metaData.getColumnName (columnNumber); מחרוזת typeName = metaData.getColumnTypeName (columnNumber); int type = metaData.getColumnType (columnNumber); מחרוזת tableName = metaData.getTableName (columnNumber); מחרוזת schemaName = metaData.getSchemaName (columnNumber); בוליאני isAutoIncrement = metaData.isAutoIncrement (columnNumber); isCaseSensitive = בוליאני = metaData.isCaseSensitive (columnNumber); isCurrency בוליאני = metaData.isCurrency (columnNumber); בוליאני isDefiniteWritable = metaData.isDefinitelyWritable (columnNumber); isReadOnly בוליאני = metaData.isReadOnly (columnNumber); ניתן לחפש בוליאני = metaData.isSearchable (columnNumber); בוליאני isReadable = metaData.isReadOnly (columnNumber); isSigned בוליאני = metaData.isSigned (columnNumber); isWritable בוליאני = metaData.isWritable (columnNumber); int nullable = metaData.isNullable (columnNumber); }

4. ניווט ב- ResultSet

כאשר אנו משיגים א ResultSet, מיקום הסמן נמצא לפני השורה הראשונה. יתר על כן, כברירת מחדל, ResultSet נע רק בכיוון קדימה. אבל, אנו יכולים להשתמש בגלילה ResultSet לאפשרויות ניווט אחרות.

בחלק זה נדון באפשרויות הניווט השונות.

4.1. ResultSet סוגים

ResultSet סוג מציין כיצד נעבור דרך מערך הנתונים:

  • TYPE_FORWARD_ONLY - אפשרות ברירת המחדל, בה הסמן עובר מתחילתו ועד סופו
  • TYPE_SCROLL_INSENSITIVE - הסמן שלנו יכול לעבור דרך מערך הנתונים בכיוונים קדימה ואחורה; אם יש שינויים בנתונים הבסיסיים בעת מעבר דרך מערך הנתונים, הם מתעלמים; מערך הנתונים מכיל את הנתונים מרגע שאילתת מסד הנתונים מחזירה את התוצאה
  • TYPE_SCROLL_SENSITIVE - בדומה לסוג שאינו רגיש לגלילה, אולם עבור סוג זה, מערך הנתונים משקף מיד כל שינוי בנתונים הבסיסיים

לא כל מאגרי המידע תומכים בכל ResultSet סוגים. אז בואו נבדוק אם הסוג נתמך באמצעות ה- supportsResultSetType על שלנו DatabaseMetaData לְהִתְנַגֵד:

DatabaseMetaData dbmd = dbConnection.getMetaData (); isSupported בוליאני = dbmd.supportsResultSetType (ResultSet.TYPE_SCROLL_INSENSITIVE);

4.2. ערכת תוצאות גלילה

כדי להשיג גלילה ResultSet, אנחנו צריכים העבירו כמה פרמטרים נוספים בזמן הכנת ה- הַצהָרָה.

לדוגמה, היינו מקבלים גלילה ResultSet על ידי שימוש באחד מהם TYPE_SCROLL_INSENSITIVE אוֹ TYPE_SCROLL_SENSITIVE כ ResultSet סוּג:

PreparedStatement pstmt = dbConnection.prepareStatement ("בחר * מעובדים", ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery (); 

4.3. אפשרויות ניווט

אנו יכולים להשתמש באחת מהאפשרויות הבאות על גלילה ResultSet:

  • הַבָּא() - ממשיך לשורה הבאה מהעמדה הנוכחית
  • קודם() - עובר לשורה הקודמת
  • ראשון() - מנווט לשורה הראשונה של ResultSet
  • אחרון() - קופץ לשורה האחרונה
  • beforeFirst () - עובר להתחלה; יִעוּד הַבָּא() על שלנו ResultSet לאחר קריאה לשיטה זו מחזירה את השורה הראשונה משלנו ResultSet
  • afterLast () - מזנק עד הסוף; יִעוּד הקודם () ב- ResultSet שלנו לאחר ביצוע שיטה זו מחזירה את השורה האחרונה משלנו ResultSet
  • יחסי (int numOfRows) - קדימה או אחורה מהמיקום הנוכחי על ידי numOfRows
  • מוחלט (int rowNumber) - קופץ אל שורה מספר נָקוּב

בואו נראה כמה דוגמאות:

PreparedStatement pstmt = dbConnection.prepareStatement ("בחר * מעובדים", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery (); בעוד (rs.next ()) {// לחזור על התוצאות מהראשון עד האחרון} rs.beforeFirst (); // קופץ חזרה לנקודת ההתחלה, לפני השורה הראשונה rs.afterLast (); // קופץ לסוף תוצאות החיפוש rs.first (); // מנווט לשורה הראשונה rs.last (); // עובר לשורה האחרונה rs.absolute (2); // קופץ לשורה השנייה rs.relative (-1); // קופץ לשורה הקודמת rs.relative (2); // קופץ קדימה שתי שורות ואילו (rs.previous ()) {// חוזר מהשורה הנוכחית לשורה הראשונה בכיוון אחורה} 

4.4. ResultSet ספירת שורות

בואו נשתמש getRow () כדי לקבל את מספר השורה הנוכחי שלנו ResultSet.

ראשית, נעבור לשורה האחרונה של ה- ResultSet ואז השתמש getRow () כדי להשיג את מספר הרשומות:

rs.last (); int rowCount = rs.getRow ();

5. עדכון נתונים בא ResultSet

כברירת מחדל, ה- ResultSet הוא לקריאה בלבד. עם זאת, אנו יכולים להשתמש בתיקון הניתן לעדכון ResultSet כדי להוסיף, לעדכן ולמחוק את השורות.

5.1. ResultSet מקביליות

מצב המקביליות מציין אם שלנו ResultSet יכול לעדכן את הנתונים.

ה CONCUR_READ_ONLY האפשרות היא ברירת המחדל ויש להשתמש בה אם איננו צריכים לעדכן את הנתונים באמצעות שלנו ResultSet.

עם זאת, אם עלינו לעדכן את הנתונים שלנו ResultSet, אז ה CONCUR_UPDATABLE יש להשתמש באופציה.

לא כל מאגרי המידע תומכים בכל מצבי המקביליות לכולם ResultSet סוגים. לכן, עלינו לבדוק אם התמיכה הרצויה בסוג ובמצב המקביליות שלנו supportsResultSetConcurrency () שיטה:

DatabaseMetaData dbmd = dbConnection.getMetaData (); בוליאני isSupported = dbmd.supportsResultSetConcurrency (ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); 

5.2. השגת עדכון ResultSet

כדי להשיג עדכון ResultSet, עלינו להעביר פרמטר נוסף כאשר אנו מכינים את הַצהָרָה. לשם כך, בואו נשתמש CONCUR_UPDATABLE כפרמטר השלישי בעת יצירת משפט:

PreparedStatement pstmt = dbConnection.prepareStatement ("בחר * מעובדים", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery ();

5.3. מעדכן שורה

בחלק זה נעדכן שורה באמצעות הניתן לעדכון ResultSet נוצר בסעיף הקודם.

אנו יכולים לעדכן נתונים ברצף על ידי התקשרות updateX () שיטות, העברת שמות העמודים והערכים לעדכון. אנו יכולים להשתמש בכל סוג נתונים נתמך במקום איקס בתוך ה updateX () שיטה.

בואו נעדכן את "שכר" טור, שהוא מסוג לְהַכפִּיל:

rs.updateDouble ("משכורת", 1100.0);

שים לב שזה פשוט מעדכן את הנתונים ב- ResultSet, אך השינויים טרם נשמרו בחזרה למסד הנתונים.

לבסוף, בואו נתקשר updateRow () ל שמור את העדכונים במסד הנתונים:

rs.updateRow (); 

במקום שמות העמודות נוכל להעביר את אינדקס העמודות ל- updateX () שיטות. זה דומה לשימוש באינדקס העמודות לקבלת הערכים באמצעות getX () שיטות. מעביר את שם העמודה או את האינדקס אל ה- updateX () שיטות מניבות את אותה התוצאה:

rs.updateDouble (4, 1100.0); rs.updateRow (); 

5.4. הכנסת שורה

עכשיו, בואו נכניס שורה חדשה באמצעות העדכון שלנו ResultSet.

ראשית, נשתמש moveToInsertRow () כדי להזיז את הסמן כדי להוסיף שורה חדשה:

rs.moveToInsertRow ();

לאחר מכן עלינו להתקשר updateX () שיטות להוסיף את המידע לשורה. עלינו לספק נתונים לכל העמודות בטבלת מסד הנתונים. אם אנו לא מספקים נתונים לכל עמודה, נעשה שימוש בערך העמודה המוגדר כברירת מחדל:

rs.updateString ("שם", "Venkat"); rs.updateString ("עמדה", "DBA"); rs.updateDouble ("משכורת", 925.0);

ואז, בוא נתקשר insertRow () להכניס שורה חדשה למסד הנתונים:

rs.insertRow ();

לבסוף, בואו נשתמש moveToCurrentRow (). זה יחזיר את מיקום הסמן לשורה בה היינו לפני שהתחלנו להכניס שורה חדשה באמצעות ה- moveToInsertRow () שיטה:

rs.moveToCurrentRow ();

5.5. מחיקת שורה

בחלק זה נמחק שורה באמצעות העדכון שלנו ResultSet.

ראשית, נעבור לשורה שאנו רוצים למחוק. ואז נקרא ל- מחק שורה() שיטה למחיקת השורה הנוכחית:

rs.absolute (2); rs.deleteRow ();

6. יכולת אחיזה

יכולת ההחזקה קובעת אם שלנו ResultSet יהיה פתוח או סגור בתום עסקת מסד נתונים.

6.1. סוגי אחזקה

להשתמש CLOSE_CURSORS_AT_COMMIT אם ה ResultSet אינו נדרש לאחר ביצוע העסקה.

להשתמש HOLD_CURSORS_OVER_COMMIT כדי ליצור אחיזה ResultSet. אחיזה ResultSet אינו נסגר גם לאחר ביצוע עסקת מסד הנתונים.

לא כל מאגרי המידע תומכים בכל סוגי האחזקה.

אז בואו בדוק אם סוג האחיזה נתמך באמצעות supportsResultSetHoldability () על שלנו DatabaseMetaData לְהִתְנַגֵד. לאחר מכן, נקבל את יכולת ברירת המחדל של מסד הנתונים באמצעות getResultSetHoldability ():

בוליאני isCloseCursorSupported = dbmd.supportsResultSetHoldability (ResultSet.CLOSE_CURSORS_AT_COMMIT); בוליאני isOpenCursorSupported = dbmd.supportsResultSetHoldability (ResultSet.HOLD_CURSORS_OVER_COMMIT); defaultHoldability בוליאני = dbmd.getResultSetHoldability ();

6.2. ניתן להחזיק ResultSet

כדי ליצור אחיזה ResultSet, עלינו לציין את יכולת אחיזה הקלד כפרמטר האחרון בזמן יצירת a הַצהָרָה. פרמטר זה מוגדר לאחר מצב המקביליות.

שים לב שאם אנו משתמשים ב- Microsoft SQL Server (MSSQL), עלינו להגדיר אחיזה בחיבור מסד הנתונים, במקום ב- ResultSet:

dbConnection.setHoldability (ResultSet.HOLD_CURSORS_OVER_COMMIT);

בואו נראה את זה בפעולה. ראשית, בואו ניצור a הַצהָרָה, קביעת יכולת ההחזקה ל HOLD_CURSORS_OVER_COMMIT:

הצהרה pstmt = dbConnection.createStatement (ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE, ResultSet.HOLD_CURSORS_OVER_COMMIT)

כעת, בואו נעדכן שורה תוך כדי אחזור הנתונים. זה דומה לדוגמת העדכון עליה דנו קודם, אלא שנמשיך לחזור דרך ה- ResultSet לאחר ביצוע עסקת העדכון למסד הנתונים. זה עובד בסדר גם במאגרי המידע MySQL וגם ב- MSSQL:

dbConnection.setAutoCommit (שקר); ResultSet rs = pstmt.executeQuery ("בחר * מעובדים"); בעוד (rs.next ()) {if (rs.getString ("שם"). equalsIgnoreCase ("ג'ון")) {rs.updateString ("שם", "ג'ון דו"); rs.updateRow (); dbConnection.commit (); }} rs.last (); 

ראוי לציין כי MySQL תומך בלבד HOLD_CURSORS_OVER_COMMIT. אז, גם אם אנו משתמשים CLOSE_CURSORS_AT_COMMIT, זה יתעלם.

מסד הנתונים של MSSQL תומך CLOSE_CURSORS_AT_COMMIT. משמעות הדבר היא כי ResultSet ייסגר כאשר אנו מבצעים את העסקה. כתוצאה מכך, ניסיון לגשת ל- ResultSet לאחר ביצוע העסקה תוצאות 'הסמן אינו שגיאה פתוחה'. לכן, איננו יכולים לאחזר רשומות נוספות מה- ResultSet.

7. גודל אחזור

בדרך כלל, בעת טעינת נתונים ל- ResultSetמנהלי מסדי הנתונים מחליטים על מספר השורות שייאספו ממסד הנתונים. על בסיס נתונים של MySQL, למשל, ה- ResultSet בדרך כלל טוען את כל הרשומות בזיכרון בבת אחת.

לפעמים, עם זאת, ייתכן שנצטרך להתמודד עם מספר רב של רשומות שלא יתאימו לזיכרון ה- JVM שלנו. במקרה זה, אנו יכולים להשתמש במאפיין גודל אחזור גם אצלנו הַצהָרָה אוֹ ResultSet אובייקטים להגביל את מספר הרשומות שהוחזרו בתחילה.

בכל פעם שנדרשות תוצאות נוספות, ResultSet מביא קבוצה נוספת של רשומות ממסד הנתונים. באמצעות המאפיין גודל אחזור, אנו יכולים ספק הצעה לנהג מסד הנתונים על מספר השורות שאחזור לכל נסיעת מסד נתונים. גודל האחזור שאנו מציינים יוחל על הנסיעות הבסיסיות לאחר מכן.

אם לא נציין את גודל האחזור עבור שלנו ResultSetואז גודל האחזור של ה- הַצהָרָה משמש. אם לא נציין גודל אחזור לאף אחד מה- הַצהָרָה או ה ResultSet, ואז נעשה שימוש בברירת המחדל של מסד הנתונים.

7.1. באמצעות גודל אחזור מופעל הַצהָרָה

עכשיו, בואו נראה את גודל האחזור הַצהָרָה בִּפְעוּלָה. נגדיר את גודל האחזור של ה- הַצהָרָה ל -10 רשומות. אם השאילתה שלנו מחזירה 100 רשומות, יהיו 10 נסיעות הלוך ושוב של בסיס נתונים, ויטענו 10 רשומות בכל פעם:

PreparedStatement pstmt = dbConnection.prepareStatement ("בחר * מעובדים", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); pstmt.setFetchSize (10); ResultSet rs = pstmt.executeQuery (); בעוד (rs.next ()) {// לחזור דרך תוצאות התוצאות}

7.2. באמצעות גודל אחזור מופעל ResultSet

עכשיו, בואו לשנות את גודל האחזור בדוגמה הקודמת שלנו באמצעות ה- ResultSet.

ראשית, נשתמש בגודל האחזור שלנו הַצהָרָה. זה מאפשר שלנו ResultSet לטעון תחילה 10 רשומות לאחר ביצוע השאילתה.

לאחר מכן נשנה את גודל האחזור ב- ResultSet. זה יעקוף את גודל האחזור שציינו קודם לכן על שלנו הַצהָרָה. אז כל הנסיעות הבאות יטענו 20 רשומות עד שכל הרשומות נטענו.

כתוצאה מכך, יהיו רק 6 נסיעות בסיס נתונים לטעינת כל הרשומות:

PreparedStatement pstmt = dbConnection.prepareStatement ("בחר * מעובדים", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); pstmt.setFetchSize (10); ResultSet rs = pstmt.executeQuery (); rs.setFetchSize (20); בעוד (rs.next ()) {// לחזור דרך קבוצת התוצאות}

לבסוף נראה כיצד לשנות את גודל האחזור של ה- ResultSet תוך איטרציה של התוצאות.

בדומה לדוגמא הקודמת, ראשית נגדיר את גודל האחזור ל -10 אצלנו הַצהָרָה. אז, 3 הנסיעות הראשונות שלנו בבסיס הנתונים יטענו 10 רשומות לכל נסיעה.

ואז נשנה את גודל האחזור על שלנו ResultSet עד 20 תוך כדי קריאת התקליט ה -30. אז, 4 הטיולים הבאים יטענו 20 רשומות לכל נסיעה.

לכן נצטרך 7 נסיעות בסיס נתונים כדי לטעון את כל 100 הרשומות:

PreparedStatement pstmt = dbConnection.prepareStatement ("בחר * מעובדים", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); pstmt.setFetchSize (10); ResultSet rs = pstmt.executeQuery (); int rowCount = 0; בעוד (rs.next ()) {// לחזור דרך קבוצת התוצאות אם (rowCount == 30) {rs.setFetchSize (20); } rowCount ++; }

8. מסקנה

במאמר זה ראינו כיצד להשתמש ב- ResultSet ממשק API לאחזור ועדכון נתונים ממסד נתונים. כמה מהתכונות המתקדמות שדנו בהן תלויות במסד הנתונים שאנו משתמשים בו. לפיכך, עלינו לבדוק את התמיכה בתכונות אלה לפני שנשתמש בהן.

כמו תמיד, הקוד זמין ב- GitHub.