Mybatis mapper encapsulates data classes

Posted by bijukon on Tue, 04 Jan 2022 18:43:00 +0100

1.1 project preparation

mybatis framework analysis

1.1. 1 project environment

  1. sqlMapConfig.xml core configuration file to remove DTD constraints. Because dom4j will go online to find DTD files.
  2. UserMapper.xml Mapping configuration file, remove DTD constraints.
  3. UserMapper interface.
  4. User entity class.

Import related jar packages

UserMapper.xml

5.1. 2 code implementation

  • Create package CN guardwhy. framework.
  • Create entity class: Mapper contains four attributes: namespace, ID, resulttype and SQL.
  • Rewrite the toString() method to facilitate the post test to see the encapsulated results.
  • Generate get and set methods, and a Mapper object represents a query statement object to be operated.
package cn.guardwhy.framework;
/**
 * Encapsulate usermapper XML attribute
 */
public class Mapper {
    private String namespace; // Encapsulation interface name
    private String id; // Method name
    private String resultType; // Returns the entity class type
    private String sql; // SQL statement to execute

    /**
     * get.set method
     * @return
     */
    public String getNamespace() {
        return namespace;
    }

    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    @Override
    public String toString() {
        return "Mapper{" +
                "namespace='" + namespace + '\'' +
                ", id='" + id + '\'' +
                ", resultType='" + resultType + '\'' +
                ", sql='" + sql + '\'' +
                '}';
    }
}

5.2 dom4j method

Parse the XML file to get the Document object

1. Get input stream InputStream
2. new SAXReader().read(Input stream) return Document object

Document common methods

Method name explain
Element getRootElement() Get the root element (tag) in XML
List<Node> selectNodes(String xpath) Query multiple node nodes through xpath. Node is the parent interface of Element
Node selectSingleNode(String xpath) Get a node through xpath
Element element(String name) Get a child element of an element by its name

Attribute text related methods

Method name Function description
String attributeValue(String name) Get the value of the attribute through the attribute name of the tag
String getTextTrim() Get the text content in the label and remove the spaces before and after

5.3 core configuration file

Encapsulate the core configuration file: sqlmapconfig XML file

  1. Create four attributes: driver, URL, username and password
  2. Instantiate an empty Map collection: encapsulate the XML information of other mapping files
  3. Declare data source object DataSource
  4. Generate get and set methods and toString() methods

loadSqlMapConfig() method

1. Create the loadSqlMapConfig() method to

  • Parsing sqlmapconfig XML Configuration file to assign values to the attributes in Configuration.
  • Resolve usermapper XML configuration file to assign values to attributes in Mapper.

2. Calling method in construction method: loadSqlMapConfig()

5.3. 1 code implementation

Core profile

package cn.guardwhy.framework;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import javax.sql.DataSource;
import javax.xml.parsers.SAXParser;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *  Parsing XML file: sqlmapconfig xml, UserMapper. xml
 */
public class Configuration {
    // 1. Create connection pool properties
    private String driver;
    private String url;
    private String username;
    private String password;
    // 2. Define connection pool
    private DataSource dataSource;

    // 3. Instantiate an empty Map set: encapsulate the XML information of other mapping files
    private Map<String, Mapper> mappers = new HashMap<>();

    // 4. call the method in the construction method: loadSqlMapConfig()
    public Configuration() {
        try {
            loadSqlMapConfig();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 5. Method of parsing configuration file
    private void loadSqlMapConfig() throws DocumentException {
        // 5.1.  Load / sqlmapconfig. From the classpath XML configuration file, creating input stream
        InputStream inputStream = Configuration.class.getResourceAsStream("/sqlMapConfig.xml");
        // 5.2.  Using dom4j to get the document object
        Document document = new SAXReader().read(inputStream);
        // 5.3.  Use XPath to read all property elements
        List<Node> nodes = document.selectNodes("//property");
        // 5.4.  Traverse each property element and read its name and value attribute values
        for(Node node : nodes){
            Element propertyElement = (Element) node;
            // Get the name attribute
            String name = propertyElement.attributeValue("name");
            // Get value attribute
            String value = propertyElement.attributeValue("value");

            // 6. Judge the string of name. If it is the same as the attribute name in the class, assign it to the corresponding attribute
            switch (name){
                case "driver":
                    this.driver = value;
                    break;
                case "url":
                    this.url = value;
                    break;
                case "username":
                    this.username = value;
                    break;
                case "password":
                    this.password = value;
                    break;
            }
        }

    }

    /***
     * set.get method
     * @return
     */
    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Map<String, Mapper> getMappers() {
        return mappers;
    }

    public void setMappers(Map<String, Mapper> mappers) {
        this.mappers = mappers;
    }

    @Override
    public String toString() {
        return "Configuration{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", dataSource=" + dataSource +
                ", mappers=" + mappers +
                '}';
    }
}

Test code

package cn.guardwhy.test;

import cn.guardwhy.framework.Configuration;

public class TestFramework {
    public static void main(String[] args) {
        Configuration configuration = new Configuration();
        System.out.println(configuration);
    }
}

5.4 entity class mapping file

Resolve usermapper XML and encapsulated in Mapper class

  1. Create a new method loadMapper(Document document) and pass the current document object to the method
  2. Read the value of the resource attribute in < mapper >
  3. Read its corresponding XML file through resource
  4. Get the values of namespace, ID, resulttype and SQL and encapsulate them into Mapper objects
  5. Call this method in loadSqlMapConfig().

5.4. 3 code implementation

Parse configuration file

// Method of parsing configuration file
private void loadSqlMapConfig() throws DocumentException {
    // Resolve usermapper XML file
    loadMapper(document);
}

/**
     * Parsing xml entity class mapping file
     * @param document
     */
private void loadMapper(Document document) throws DocumentException{
    // 1. Read the resource attribute value in mapper
    // 1.1 read mapper element
    List<Node> nodes = document.selectNodes("//mapper");
    // 1.2 traverse each mapper element
    for (Node node : nodes){
        Element mapperElement = (Element) node;
        // 1.3 read the resource attribute value of mapper
        String resource = mapperElement.attributeValue("resource");
        // 2. Parse the XML file to get the values of namespace, ID, resulttype and SQL

        // 2.1 use the class object to read the resource under the input stream
        InputStream inputStream = Configuration.class.getResourceAsStream("/" + resource);
        // 2.2 creating document objects
        Document doc = new SAXReader().read(inputStream);
        // 2.3 get root element
        Element rootElement = doc.getRootElement();
        // 2.4 get the namespace attribute
        String namespace = rootElement.attributeValue("namespace");
        // 2.5 read a select tag under the root element
        Element selectElement = rootElement.element("select");
        // 2.6 get the id attribute
        String id = selectElement.attributeValue("id");
        // 2.7 resultType attribute
        String resultType = selectElement.attributeValue("resultType");
        // 2.8 SQL properties
        String sql = selectElement.getTextTrim();

        // 3. Package as Mapper object
        // 3.1 create a custom Mapper object to encapsulate the above three attributes
        Mapper mapper = new Mapper();
        mapper.setId(id);
        mapper.setResultType(resultType);
        mapper.setSql(sql);
        // 3.2 repackaging namespace attribute
        mapper.setNamespace(namespace);
        // 3.3 add the encapsulated mapper object to the mappers attribute of this, where the key is namespace + "+ ID, the value is a custom mapper object.
        String key = namespace + "." + id;
        this.mappers.put(key, mapper);
    }

}

Test code

package cn.guardwhy.test;

import cn.guardwhy.framework.Configuration;

public class TestFramework {
    public static void main(String[] args) {
        Configuration configuration = new Configuration();
        System.out.println(configuration);
    }
}

5.5 creating data sources

  1. Create c3p0 a new data source. The data source class is ComboPooledDataSource.
  2. Set database related properties: driver, url,username,password.
  3. Set the dataSource of this as the data source object created above.

5.5. 2 code implementation

/**
* 4.In the construction method, the method is called: 
*  loadSqlMapConfig()
*  Call the createDataSource() method
*/
public Configuration() {
    try {
        loadSqlMapConfig();
        createDataSource();  //create data source
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
  create data source
*/
private void createDataSource() throws PropertyVetoException {
    //Using c3p0 connection pool
    ComboPooledDataSource ds = new ComboPooledDataSource();
    //Set the properties of the connection pool in the code
    ds.setUser(this.username);
    ds.setPassword(this.password);
    ds.setJdbcUrl(this.url);
    ds.setDriverClass(this.driver);
    //The created data source is assigned to the member variable
    this.dataSource = ds;
}

5.6 core component SqlSession

Generation steps

  1. Write the SqlSession class and provide a getMapper() method to obtain the implementation object (proxy object) of the interface.
  2. Test: call the methods in the interface. The method of querying the database does not query from the database, but writes the simulated data in the code.

JDK dynamic proxy benefits

  1. The proxy object of the interface is dynamically generated by the program during execution. We don't need to write a class to implement all the methods in the interface

  2. You can dynamically generate objects with any interface

Methods in Proxy class

Proxy.newProxyInstance(): creates a dynamic proxy object for the UserMapper interface.

Parameter list: static object newproxyinstance (classloader, loader, class [] interfaces, invocationhandler h)
Role: dynamically generate proxy objects.

loader The same class loader as the real object
interfaces Interfaces implemented by proxy classes
h Call the interface of the proxy object, and pass in an implementation class when using.
You need to rewrite the methods in the interface to realize the call of each method in the real object.
return Generate proxy object

InvocationHandler interface

Object invoke(Object proxy, Method method, Object[] args)
Function: this method in the interface will be called multiple times, and each proxied method in the real object will be called once

proxy For dynamically generated proxy objects, do not call them directly in methods, otherwise recursive dead loop calls will occur.
method Methods of real objects
args Parameters passed when a proxy object calls a method
return Method

invoke() method

method.invoke(Object obj, Object[] args) Each method in a real object is called through reflection
Object obj Real object
Object[] args Parameters passed when calling a real method

Method signature

public <T> T getMapper(Class<T> type) 

InvocationHandler anonymous class

Analysis diagram

basic function

  • Get the Mapper object through the key.
  • Get the SQL statement execution from the Mapper object and wrap it into an object return.

Generation steps

  • Instantiate the Configuration object through the class full name + "+ Get the key for the method name.
  • Get the value Mapper object through the key to get the sql statement to be executed and the returned entity type.
  • Get the connection object through the data source, perform database operations, encapsulate the result set through reflection and return.

5.6.1 SqlSession class

Get SQL statement and return type

  • Get the Map collection in Configuration
instantiation  Configuration object
 adopt Configuration obtain Mapper Collection of objects
  • Get the key in the Map: the full name of the class Method name
By method object->Get declared interface->Get the name: that is, the full name of the class com.itheima.dao.UserMapper
 Get the name of the currently executed method: findAllUsers
 Pass class full name+Method name get key
  • Get the corresponding properties in Mapper
Pass class full name+"."+Method name, from mappers Mapped in mapper object
 from mapper Get the of the query from sql sentence
 from mapper Get return value type from resultType
 By reflection resultType The string is converted into a class object for later methods

Object Access database

  • Get the data source through Configuration and the connection object through the data source
  • Call the list queryforlist (connection, string SQL, class clazz) method
1. use JDBC Query data from database
2. Use reflection to instantiate clazz Object, and encapsulates all attributes and adds them to the collection.

Illustration:

5.6.2 JDBC access database

Create collection List Encapsulate result set without generics
 adopt Connection Connection object creates a precompiled statement object
 Execute the query to get the result set ResultSet

Encapsulate data into List objects

5.6. 3 code example

Session session class

package cn.guardwhy.framework;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Session class
 */
public class SqlSession {
    /**
     * Create a proxy object for the UserMapper interface
     * @param mapperClass Interface class object
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> mapperClass){
        return (T) Proxy.newProxyInstance(SqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            /***
             *
             * @param proxy Generated proxy object
             * @param method   Method to call
             * @param args Method parameters
             * @return Return value: the return value of the method
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 1. Create Configuration object
                Configuration configuration = new Configuration();
                // 2. Get the name of the method
                String id = method.getName();
                // 3. Get the name of the interface
                String namespace = method.getDeclaringClass().getName();
                // 4. Get the key value
                String key = namespace + "." + id;
                // 5. Get value
                Map<String, Mapper> mappers = configuration.getMappers();
                Mapper mapper = mappers.get(key);
                // 6.SQL statement
                String sql = mapper.getSql();
                // 7. Get the returned data type
                String resultType = mapper.getResultType();
                // 8. Get its class object
                Class objClass = Class.forName(resultType);
                // 9. The Connection object is required to access the database
                DataSource dataSource = configuration.getDataSource();
                Connection connection = dataSource.getConnection();

                // Use JDBC to access the database and encapsulate it as list < user >
                List list = queryForList(connection, sql, objClass);
                return list;
            }
        });
    }

    /**
    Use JDBC to access the database and encapsulate it as list < user >
     */
    private List queryForList(Connection connection, String sql, Class clazz) throws Exception{
        List users = new ArrayList<>();
        // 1. Get the precompiled statement object through the connection object
        PreparedStatement ps = connection.prepareStatement(sql);
        // 2. Execute the SQL statement to get the result set
        ResultSet rs = ps.executeQuery();
        // 3. Traverse the result set and encapsulate each row of records into a User object
        while (rs.next()){
            Object user = clazz.getConstructor().newInstance();
            // Get all member variables in the class
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields){
                // Get the name of the member variable
                String name = field.getName();
                // Traverse member variables and assign values to each member variable
                field.setAccessible(true);
                // Get all the data from the result set
                field.set(user, rs.getObject(name));
            }
            // 4. Add to collection
            users.add(user);
        }

        rs.close();
        ps.close();
        connection.close();
        // 5. Return set
        return users;
    }
}

Test class

package cn.guardwhy.test;

import cn.guardwhy.dao.UserMapper;
import cn.guardwhy.domain.User;
import cn.guardwhy.framework.Configuration;
import cn.guardwhy.framework.SqlSession;

import java.util.List;

public class TestFramework {
    public static void main(String[] args) {
        // 1. Use SqlSession class
        SqlSession session = new SqlSession();
        // 2. Call getMapper(UserMapper.class), and the proxy object will be returned
        UserMapper userMapper = session.getMapper(UserMapper.class);
        System.out.println(userMapper.getClass());
        // 3. Call the method of the proxy object to get all users
        List<User> users = userMapper.findAllUsers();
        // 4. Output user
        users.forEach(System.out::println);
    }
}

Topics: Mybatis