How ibatis works

Abstract: iBATIS uses SQL Map to map Java objects into SQL statements and convert result sets into Java objects. Compared with other ORM frameworks, iBATIS not only solves the mapping between Java objects and input parameters and result sets, but also allows users to easily Handwritten using SQL statements. This article mainly introduces the architecture and running process of iBATIS framework, as well as how iBATIS completes the parsing of SQL statements and the establishment of the mapping relationship between Java objects and data fields.

The main class hierarchy of the iBATIS framework

Generally speaking, the system structure of iBATIS is relatively simple. It mainly accomplishes two things:

  1. Establish a connection to the database according to the JDBC specification;

  2. Through reflection, the mutual conversion relationship between Java objects and database parameter interaction is opened up.

The framework structure of iBATIS also organizes the class hierarchy according to this idea. In fact, it is a typical interactive framework. Prepare the necessary conditions for interaction in advance, and then build an interactive environment. The interactive environment is also divided into sessions, and each session also has an environment. When these environments are ready, all that's left is to exchange data. In fact, when it comes to network communication, it is generally handled in a similar way.

Figure 1 is the main class hierarchy diagram of the iBATIS framework:

Figure 1. The main class hierarchy diagram of the iBATIS framework

Figure 1. The main class hierarchy diagram of the iBATIS framework

The SqlMapClient interface on the left in the above class diagram mainly defines the operation behavior of the client, including select, insert, update, and delete. The right side mainly defines the execution environment of the current client in the current thread. SqlMapSession can be shared or created by yourself. If you create it yourself, you must call the close interface to close it at the end.

When the user holds the SqlMapClientImpl object, iBATIS can be used to work. Another class, SqlMapExecutorDelegate, is also mentioned here. This class can be seen from the name that he is an execution proxy class. This class is very important because it couples the user-side execution behavior and execution environment, it holds the data needed to execute the operation, and provides an environment that manages the dependencies of the execution. Therefore, it is a strongly coupled class, which can also be regarded as a tool class.

back to top

Design Strategies for the iBATIS Framework

The main design purpose of iBATIS is to make the data management of input and output more convenient when we execute SQL, so how to easily let us write SQL and easily obtain the execution result of SQL is the core competitiveness of iBATIS. So how does iBATIS achieve its core competitiveness?

An important part of the iBATIS framework is its SqlMap configuration file. The core of the SqlMap configuration file is the Statement statement including CIUD. iBATIS obtains all Statement execution statements by parsing the SqlMap configuration file, and forms two objects, ParameterMap and ResultMap, which are used to process parameters and the Sql objects that are parsed and handed over to the database for processing. In this way, the connection of the database is removed, and the execution conditions of a SQL are already satisfied.

Figure 2 depicts the class diagram related to Statement:

Figure 2. Class structure diagram related to Statement

Figure 2. Class structure diagram related to Statement

Figure 2 shows the basic structural relationship around SQL execution, but another key part is how to define the relationship between the parameters in the SQL statement and the Java objects, which also involves the conversion of Java types to database types and a series of questions.

The general process of data mapping is as follows: According to the SQL statement defined in the Statement, parse out the parameters, save them in the Map collection in the order of their appearance, and parse out the Java data of the parameters according to the ParameterMap object type defined in the Statement Types of. And build the TypeHandler object according to its data type, the copying of the parameter value is done through the DataExchange object.

Figure 3 is a class structure diagram related to parameter mapping:

Figure 3. Class structure diagram related to parameter mapping

Figure 3. Class structure diagram related to parameter mapping

Figure 3 shows the mapping structure of the input parameters, and the mapping of the returned result ResultMap is similar. The main purpose is to solve the corresponding relationship between the parameters in the SQL statement and the column name of the returned result and the attributes in the parameterClass and resultClass defined in the Statement.

back to top

How the iBATIS Framework Works

The structure of the main classes of the iBATIS framework has been roughly analyzed. Here we mainly look at how these classes are connected in series and how they work. Figure 4 describes the main execution steps of the entire process.

Figure 4. Main execution steps of iBATIS operation

Figure 4. Main execution steps of iBATIS operation

The creation and release of the SqlMapSession object described in the above figure will vary according to different situations, because SqlMapSession is responsible for creating database connections, including transaction management. iBATIS can manage transactions by itself or externally. By sharing the SqlMapSession object, the execution of multiple Statements shares a SqlMapSession instance, and they are all thread-safe. If it is an external program management, it is necessary to control the life cycle of the SqlMapSession object by itself.

Figure 5 is a detailed sequence diagram of calling iBATIS through Spring to execute a Statement:

Figure 5. The sequence diagram of Spring calling iBATIS to execute a Statement

Figure 5. The sequence diagram of Spring calling iBATIS to execute a Statement

(View a  clearer version of Figure 5. )

The main work of iBATIS is connection and interaction, so different transaction environments must be designed according to different transaction costs.

back to top

Example

Next, we will analyze how a Statement completes the mapping according to a specific example. We use a typical query statement to see how the data in the Java object is assigned to the parameters in SQL, and then look at how the query result of SQL is converted. into a Java object.

Let's take a look at part of the code and configuration file of the example first, and please see the attachment for the complete code.

Spring's applicationContext configuration file:

Listing 1. applicationContext.xml
<beans>
    <bean id="sqlMapTransactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean id="sqlMapTransactionTemplate" 
        class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="sqlMapTransactionManager"/>
    </bean>
    <!--sql map -->
    <bean id="sqlMapClient" 
        class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="com/mydomain/data/SqlMapConfig.xml"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean id="dataSource" name="dataSource" 
        class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@10.1.5.11:1521:XE"/>
        <property name="username" value="junshan"/>
        <property name="password" value="junshan"/>
        <property name="maxActive" value="20"/>
    </bean>
    <bean id="accountDAO" class="com.mydomain.AccountDAO">
        <property name="sqlMapClient" ref="sqlMapClient"/>
        <property name="sqlMapTransactionTemplate" ref="sqlMapTransactionTemplate"/>
    </bean>
</beans>

Here is a Statement from Account.xml:

Listing 2. A Statement in Account.xml
<select id="selectAccount" parameterClass="Account" resultClass="Account">
    select
      ACC_ID,
      ACC_FIRST_NAME as firstName,
      ACC_LAST_NAME as lastName,
      ACC_EMAIL as emailAddress,
      ACC_DATE
    from ACCOUNT
    where ACC_ID = #id:INTEGER# and ACC_FIRST_NAME = #firstName#
</select>

Here is the test class in Java:

Listing 3. SimpleTest
public class SimpleTest {
    public static void main(String[] args) {
        ApplicationContext factory = 
        new ClassPathXmlApplicationContext("/com/mydomain/data/applicationContext.xml");
        final AccountDAO accountDAO = (AccountDAO) factory.getBean("accountDAO");
        final Account account = new Account();
        account.setId(1);
        account.setFirstName("tao");
        account.setLastName("bao");
        account.setEmailAddress("[email protected]");
        account.setDate(new Date());
        try {
            accountDAO.getSqlMapTransactionTemplate().execute(new TransactionCallback(){
                    public Object doInTransaction(TransactionStatus status){
                        try{
                            accountDAO.deleteAccount(account.getId());
                            accountDAO.insertAccount(account);
                            //account.setLastName("bobo");
                            //accountDAO.updateAccount(account);
                            Account result = accountDAO.selectAccount(account);
                            System.out.println(result);
                            return null;
                        } catch (Exception e) {
                            status.setRollbackOnly();
                            return false;
                        }
                    }
                });
            //accountDAO.getSqlMapClient().commitTransaction();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
}

back to top

iBATIS parsing of SQL statements

The SQL parsing mentioned here is only for the SQL statements defined in the iBATIS configuration file, such as the query statement shown in Listing 2 in the previous section. Unlike standard SQL statements, parameter assignments are variable names wrapped in "#". How to parse this variable is what iBATIS has to do. Of course, there are many other forms of SQL expression, such as dynamic SQL.

Now all we care about is when we execute:

Listing 4. Execute query method
accountDAO.selectAccountById(account)

iBATIS will select the Statement in Listing 2 to parse, and finally parse it into a standard SQL and submit it to the database for execution, and set two selection condition parameters. What are the details of parameter mapping in this process?

As explained in the second section above, iBATIS parses the SqlMap configuration file into Statements, including ParameterMap, ResultMap, and the parsed SQL. After iBATIS builds the RequestScope execution environment, the work to be done is to extract a parameter array by combining the passed object data with the information in ParameterMap. The order of this array corresponds to the order of parameters in SQL, and then preparedStatement.setXXX( i, parameter) submit parameters.

In Listing 3, we assign 1 and "tao" to the id attribute and firstName attribute of the account object, respectively. When the code in Listing 4 is executed, iBATIS must pass these two attribute values ​​to the SQL statement in Listing 2. parameter of the object in . How to do this is actually very simple. The relationship between the classes related to ParameterMap is described in Figure 3. These classes all save all the necessary information for parsing the Statement in Listing 2 when the SqlMap configuration file is initialized. The specific The information is this:

The final SQL statement is:

Listing 5. The parsed SQL
select
    ACC_ID,
    ACC_FIRST_NAME as firstName,
    ACC_LAST_NAME as lastName,
    ACC_EMAIL as emailAddress,
    ACC_DATE
from ACCOUNT
where ACC_ID = ? and ACC_FIRST_NAME = ?

#id:INTEGER# will be parsed as the JDBC type is INTEGER, and the parameter value takes the id attribute of the Account object. #firstName# is also parsed into the firstName property of the Account object, and parameterClass="Account" specifies the class type of Account. Notice in Listing 5 that #id:INTEGER# and #firstName# are both replaced with "?", how does iBATIS guarantee their order? In the process of parsing Listing 2, iBATIS will extract the legal variable names according to the "#" separator to construct the parameter object array, and the order of the array is the order in which the variables appear in SQL. iBATIS then creates the appropriate dataExchange and parameterPlan objects based on these variables and the type specified by parameterClass. The parameterPlan object holds a list of the variable's setter and getter methods in the preceding order.

Therefore, the assignment of parameter is to use the reflection mechanism to obtain the parameter value array corresponding to Listing 5 according to the list of getter methods saved in parameterPlan and the incoming account object, and then submit the array to the database according to the specified JDBC type. The above processes can be clearly described by the timing diagram in Figure 6:

Figure 6. Sequence diagram of mapping parameter values ​​to database process

Figure 6. Sequence diagram of mapping parameter values ​​to database process

In Figure 4 above, if the value is empty in step 8, preparedStatement.setNull(i, jdbcType) will be set. If the variable in Listing 2 is not set to the jdbcType type, there may be an error.

back to top

Database fields are mapped to Java objects

After the database executes SQL, it will return the execution result. In the example in Section 4, there are two pieces of information that satisfy the id of 1 and the firstName of "tao". How does iBATIS set these two records into the account object?

Similar to the ParameterMap, the resources needed to populate the returned information are already contained in the ResultMap. When there is a ResultSet object that saves the returned results, it is necessary to map the column names to the corresponding properties of the account object. The process is roughly as follows:

Create the return object according to the ResultClass defined in the ResultMap, here is the account object. Get all the writable attribute arrays of this object, that is, the setter method, and then match the previous attribute array according to the column names in the returned ResultSet, and construct the matching result into a set (resultMappingList), followed by selecting the DataExchange type and the AccessPlan type. Provide support for the actual data exchange that follows. According to the resultMappingList collection, the values ​​corresponding to the columns are taken out from the ResultSet to form a value array (columnValues). The order of this array is the order of the corresponding column names in SQL. Finally, set the value of columnValues ​​to the object by calling the setter method of the property of the account object. This process can be represented by the following sequence diagram:

Figure 7. Map return object sequence diagram

Figure 7. Map return object sequence diagram

back to top

The result of the example run

The first two subsections mainly describe the mapping principle of input parameters and output results. Here, we will analyze the results of executing the code in Listing 3 in combination with the example in subsection 4.

The result printed by executing the code shown in Listing 3 is:

Listing 6. Results of running the sample program
Account{id=0, firstName='tao', lastName='bobo', emailAddress='junshan@taobao.com'}

The above results seem to be different from what we expected. Looking at the code, the attribute values ​​of the account object we inserted into the database are {1, "tao", "bao", "[email protected]", "time"}, After calling the query in Listing 2, the return should be the same result. The result of id is wrong, and the value of date attribute is lost. Take a closer look at the Statement in Listing 2 and find that the column names of the returned results are {ACC_ID, firstName, lastName, emailAddress, ACC_DATE} where id and date cannot be mapped to the properties of the Account class. id is assigned the default number 0, and date is not assigned.

Another point worth noting is that the variable id is followed by the JDBC type. Is this JDBC type useful? Usually it is not useful, so you can not set it, iBATIS will automatically choose the default type. But if you want this value may be empty, if you don't specify the JDBC type, there may be a problem. Although it can work normally in Oracle, it will cause Oracle's current SQL to be compiled multiple times, so it will affect the performance of the database. Also, if the same Java type corresponds to multiple JDBC types (eg Date corresponds to JDBC types such as java.sql.Date, java.sql.Timestamp), you can save different values ​​to the database by specifying the JDBC type.

back to top

Summarize

If the most concise words to summarize the main functions of iBATIS, I think the following code is enough to summarize.

Listing 7. Typical Java code for manipulating databases
Class.forName("oracle.jdbc.driver.OracleDriver"); 
Connection conn= DriverManager.getConnection(url,user,password);
java.sql.PreparedStatement  st = conn.prepareStatement(sql);
st.setInt(0,1);
st.execute();
java.sql.ResultSet rs =  st.getResultSet();
while(rs.next()){
    String result = rs.getString(colname);
}

iBATIS decomposes and wraps the above lines of code, but these lines of code are still executed in the end. The first two lines are for the management of the data source of the database, including transaction management. Lines 3 and 4 of iBATIS manage SQL and the mapping of input parameters through the configuration file. Lines 6, 7, and 8 are the mapping from the returned results to Java objects by iBATIS. He is also managed through the configuration file.

The configuration file corresponds to the corresponding code as shown in the figure:

Figure 8. Correspondence between configuration files and corresponding codes

Figure 8. Correspondence between configuration files and corresponding codes

The purpose of iBATIS is to put the data that the user cares about and easy to change into the configuration file to facilitate user management. And leave the process and fixed to iBATIS to realize. This makes it simple and convenient for users to operate the database, which is also the value of iBATIS.

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326839793&siteId=291194637