יום ראשון, 8 באפריל 2018

ירושה ג'נרית של מחלקת ה-SQL לכל טבלה - Java

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

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







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

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










קוד:

מחלקה לייצוג שגיאה:
public class CustomError {
    public String CustomMessage;
    public String SystemMessage;
    public Boolean isThereError;
    public CustomError(){
        CustomMessage = "Good";
        SystemMessage = "Good";
        isThereError = false;
    }
}






מחלקה לייצוג עובד:
public class Employee {
    private int EmployeeID;
    private String LastName;
    private String FirstName;
    private String City;
    // (יש עוד מאפיינים לעובד אבל לצורך הדוגמה נסתפק באלו)
    
    
    
    // פונקציה בונה עם ברירת המחדל של השדות
    public Employee(){
        EmployeeID = 0;
        LastName = "No LastName";
        FirstName = "No FirstName";
        City = "No City";
    }
    
    // פונקציה בונה שמקבלת ערכים
    public Employee(int EmployeeID, String LastName, String FirstName, String City) {
        this.EmployeeID = EmployeeID;
        this.LastName = LastName;
        this.FirstName = FirstName;
        this.City = City;
    }
    
    
    
    
    
    public int getEmployeeID() {
        return EmployeeID;
    }

    public void setEmployeeID(int EmployeeID) {
        this.EmployeeID = EmployeeID;
    }

    public String getLastName() {
        return LastName;
    }

    public void setLastName(String LastName) {
        this.LastName = LastName;
    }

    public String getFirstName() {
        return FirstName;
    }

    public void setFirstName(String FirstName) {
        this.FirstName = FirstName;
    }

    public String getCity() {
        return City;
    }

    public void setCity(String City) {
        this.City = City;
    }
    
    
    @Override
    public String toString() {
        return "Employee{" + "EmployeeID=" + EmployeeID + ", LastName=" +
                LastName + ", FirstName=" + FirstName + ", City=" + City + '}';
    }
}









interface שמחייב לספק פונקציה שיודעת להמיר ResultSet לאובייקט:


public interface IResultSetToObjectAble <TypeOfSimpleClass> {
    TypeOfSimpleClass fromResultSetToNewInstance(ResultSet rs, CustomError CE);
}









ממחלקה ג'נרית זו כל המחלקות של הטבלאות יורשות:
public abstract class SQLMaster<TypeOfSimpleClass> {
    private final String userNameOfDB;
    private final String passwordOfDB;
    private final String serverName;
    protected final String dbName;
    protected final String TableName;
    
    
    
    // פונקציה בונה
    public SQLMaster(String TableName){
        userNameOfDB = "Game_User";
        passwordOfDB = "12345";
        dbName = "NORTHWND";
        serverName = "DESKTOP-RCEFUJ4";
        this.TableName = TableName;
    }
    
    
    // לקבל קישור לבסיס נתונים
    protected Connection getConnection() throws ClassNotFoundException, SQLException {
        String url = "jdbc:sqlserver://"+serverName+";databaseName="+dbName;
        
        // רישום לקלאס של הדרייבר
        Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        Connection conn;
        conn = DriverManager.getConnection(url,userNameOfDB,passwordOfDB);
        return conn;
    }
    
    
    
    // סגירה בטוחה של החיבור
    protected void closeStatementAndConnection(Connection con, Statement st){
        // סגירה של החיבורים
        try {  // רק בגלל שהסגירה עלולה להוציא שגיאה הגאווה מכריח לעשות את בלוק הטריי הזה
            if (st != null) { // כי אם נפלנו בהתחלה זה יהיה נאל
                st.close();
            }
            if (con != null) {
                if (!con.isClosed()) {  // לדוגמה אם נפל החשמל
                    con.close();
                }
            }
            
        } catch (Exception e) {
            // אין מה לכתוב כאן כי כל הבלוק טריי מיותר
        }
    }
    

    
    
    
    
    
    
    
    // numberOfLines = מספר העמודות שצריך לקרוא מבסיס הנתונים
    // אפס משמעותו להביא את כל השורות
    
    // OutList = הרשימה שאנו מחזירים על ידי שמשפיעים עליה
    // היא חייבת להיות מאותחלת
    
    // ColumnsNames = שמות העמודות שיש בטבלה ואנחנו צריכים לקרוא
    
    // ToResultSetObject = אובייקט שמכיל פונקציה שיודעת להמיר את הריזולטסט לאובייקט
    // זה נעשה בצורה זו משום שלא ניתן לשלוח מצביע לפונקציה בגאווה
    
    protected CustomError GetListOfAnyTable(
            final int numberOfLines,
            ArrayList<TypeOfSimpleClass> OutList,
            final String[] ColumnsNames,
            IResultSetToObjectAble<TypeOfSimpleClass> ToResultSetObject)
    {
        Connection con = null;
        Statement st = null;
        ResultSet rs = null;
        String selectString;
        CustomError CE = new CustomError();

                
        
        
        
        
        // בדיקה שהרשימה שקיבלנו מאותחלת
        if(OutList == null){
            CE.CustomMessage = "Error in 'GetListOfAnyTable': OutList = null";
            CE.isThereError = true;
            return CE;
        }
        
        
        
        
        // יצירת המחרוזת של השאילתה
        
        if (numberOfLines == 0) { // את כל הרשומות אם קיבלנו פרמטר אפס
            selectString = "SELECT ";
            for (int i = 0; i < ColumnsNames.length; i++) {
                String column = ColumnsNames[i];
                selectString += "[";
                selectString += column;
                selectString += "]";
                if (i < ColumnsNames.length - 1) selectString += ",";
                selectString += " ";
            }

            selectString += "FROM [" + dbName + "].[dbo].[" + TableName + "] ";
            
            
        } else { //רשומות כמספר שקיבלנו אם לא קיבלנו פרמטר אפס
            selectString = "SELECT TOP " + numberOfLines + " ";
            for (int i = 0; i < ColumnsNames.length; i++) {
                String column = ColumnsNames[i];
                selectString += "[";
                selectString += column;
                selectString += "]";
                if (i < ColumnsNames.length - 1) selectString += ",";
                selectString += " ";
            }

            selectString += " FROM ["+dbName+"].[dbo].[" + TableName +"] "; 
        }
        // עד כאן יצירת המחרוזת של השאילתה
        
        
        
        
        
        try
        {
           con = getConnection();
           st = con.createStatement();
           rs = st.executeQuery(selectString);
           

           
           while(rs.next()) // ריצה על התוצאות
           {
               TypeOfSimpleClass tmp = ToResultSetObject.fromResultSetToNewInstance(rs, CE);
               if(CE.isThereError == true){
                   break;
               }
               OutList.add(tmp); //הוספה לרשימה 
           }
        }
        catch(Exception e)
        {
            CE.CustomMessage = "Error in 'GetListOfAnyTable'";
            CE.SystemMessage = e.getMessage();
            CE.isThereError = true;
        }
        
        finally { // צריך לעשות סגירה של החיבור גם אם נפלנו באקספטשן
            closeStatementAndConnection(con, st);
        }
        
        return CE;
    }
}









מחלקה לטבלת העובדים:
public class SQLTable_Employees extends SQLMaster<Employee>{
    
    // פונקציה בונה
    public SQLTable_Employees(){
        super("Employees"); // הפעלת הפונקציה הבונה של המחלקה ממנה ירשנו
    }
    
    
    
    
    // כך עוטפים פונקציה במחלקה כדי לשלוח אותה כפרמטר
    // ClassFromResultSetToNewInstance
    class ClassFRSTNI 
    implements IResultSetToObjectAble <Employee>{

        
        // פונקציה שיודעת להפוך ריזולטסט לאובייקט
        public Employee fromResultSetToNewInstance(ResultSet rs, CustomError CE) {
            Employee tmpEmployee = new Employee();
            try {
                tmpEmployee.setEmployeeID(rs.getInt("EmployeeID"));
                tmpEmployee.setLastName(rs.getString("LastName"));
                tmpEmployee.setFirstName(rs.getString("FirstName"));
                tmpEmployee.setCity(rs.getString("City"));
            } catch (Exception e) {
                CE.isThereError = true;
                CE.CustomMessage =
                        "Error in SQLTable_Employees -> fromResultSetToNewInstance";
                CE.SystemMessage = e.getMessage();
            }
            return tmpEmployee;
        }
    }
        
        
        
        
        
        
    // numberOfLines להחזיר רשימה של כל העובדים הראשונים כמספר
    // אפס משמעותו להביא את כל הקטגוריות
    // Out_EmployeesList = רשימה שמקבלים כפרמטר שצריך להשפיע עליו ועל ידי כך להשפיע על המקור
    // הרשימה שמקבלים חייבת להיות מאותחלת כבר כדי שיהיה ניתן להשתמש בה
    public CustomError GetListOfEmployees(final int numberOfLines,
            ArrayList<Employee> Out_EmployeesList)
    {
        
        // כדי לשלוח את הפונקציה שהופכת ריזולטסט לרשימה באופן ייחודי למחלקה זו וייחודי גם לפונקציה זו
        // למעשה זו הדרך לשלוח מצביע לפונקציה
        ClassFRSTNI PFunction = new ClassFRSTNI();
        
        
        // לציין בדיוק אלו עמודות אנחנו מעוניינים לקבל מבסיס הנתונים
        String[] ColumnsNames =
        {"EmployeeID", "LastName", "FirstName", "City"};

        return GetListOfAnyTable(numberOfLines, Out_EmployeesList, ColumnsNames, PFunction);
    }
}










המחלקה הראשית (main):
public class SQL_Generics_Inheritance {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        CustomError CE;
        
        SQLTable_Employees SQLT_Emp = new SQLTable_Employees();
        ArrayList<Employee> EmployeesList = new ArrayList<Employee>();
        CE = SQLT_Emp.GetListOfEmployees(0, EmployeesList);
        
        if(CE.isThereError == true){
            System.out.println("Error:");
            System.out.println("Custom Message: " + CE.CustomMessage);
            System.out.println("System Message: " + CE.SystemMessage);
        }
        else{
            // הדפסת כל העובדים
            for (Employee emp : EmployeesList){
               System.out.println(emp); // (toString)
               System.out.println("\n");
           }
        }
        
        
        System.out.println("End!");
    }
    
    
}

להורדה של כל התוכנית: כאן, או כאן.













זה לא באמת חלק מחומר הלימודים...

אין תגובות:

הוסף רשומת תגובה