Java Software Development: Custom MyBatis persistence layer framework

1 framework overview

1.1 what is a framework

Framework is the reusable design of the whole or part of the system, which is expressed as a group of abstract components and the method of interaction between component instances; Another definition holds that a framework is an application skeleton that can be customized by application developers. The former is defined from the aspect of application, while the latter is defined from the aspect of purpose.

In short, a framework is actually a semi-finished product of an application, a set of components for you to choose and complete your own system. Simply put, it is to use the stage built by others and you perform. Moreover, the framework is generally mature and constantly upgraded software.

1.2 problems to be solved by the framework

The most important problem to be solved by the framework is the problem of technology integration. In the framework of J2EE, there are various technologies. Different software enterprises need to choose different technologies from J2EE, which makes the final application of software enterprises depend on these technologies. The complexity and risk of technology will directly impact the application.

Application is the core of software enterprises and the key to competitiveness. Therefore, the design of application itself should be decoupled from the specific implementation technology. In this way, the research and development of software enterprises will focus on the design of applications rather than specific technical implementation. Technical implementation is the bottom support of applications, and it should not directly affect applications.

The framework is generally located in the middle layer between the low-level application platform (such as J2EE) and the high-level business logic.

1.3 importance of software development layering

The importance of the framework is that it realizes some functions and can ease the low-level application platform and high-level business logic. In order to realize "high cohesion and low coupling" in software engineering. The problem is divided into various solutions, which is easy to control, easy to extend and easy to allocate resources. Our common MVC software design idea is a good layered idea.


Through layering, the responsibilities of each part are better realized. In each layer, different frameworks will be refined to solve the problems concerned by each layer.

2 MyBatis framework

mybatis is an excellent persistence layer framework based on java. It encapsulates jdbc internally, so that developers only need to pay attention to the sql statement itself, and do not need to spend energy to deal with the complicated processes such as loading driver, creating connection, creating statement and so on.

Mybatis configures various statements to be executed by means of xml or annotation, and generates the final executed sql statement by mapping the java object and the dynamic parameters of sql in the statement. Finally, the mybatis framework executes sql and maps the results into java objects and returns them.

The ORM idea is adopted to solve the problem of entity and database mapping, encapsulate jdbc and shield the underlying access details of jdbc api, so that we can complete the persistence operation of database without dealing with jdbc api.

3 JDBC programming

3.1 JDBC program review

import java.sql.*;

public class Main {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        String url = "jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8";
        String driver = "com.mysql.jdbc.Driver";
        String username = "root";
        String password = "Root@2020";
        try {
            //Load database driver
            Class.forName(driver);

            //Get database connection through driver management class
            connection = DriverManager.getConnection(url, username, password);

            //Define Sql statement,? Identity placeholder
            String sql = "select * from user where username = ?";

            //Get preprocessing statement
            preparedStatement = connection.prepareStatement(sql);

            //Set parameters. The first parameter is the sequence number of the parameter in the sql statement (starting from 1), and the second parameter is the parameter value
            preparedStatement.setString(1, "Lao Wang");

            //Execute the query on the database and return the query result set
            resultSet = preparedStatement.executeQuery();

            //Traverse the query result set
            while (resultSet.next()) {
                System.out.println(resultSet.getString("id") + "," + resultSet.getString("username"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

3.2 problems with JDBC

1. The frequent creation and release of database links cause a waste of system resources, which affects the system performance. If you use database connection pool, you can solve this problem.

2. sql statements are hard coded in the code, which makes the code difficult to maintain. The actual application of sql may change greatly. sql changes need to change the java code.

3. There is hard coding in using preparedStatement to pass parameters to the possessive symbol, because the where conditions of sql statements are not necessarily, which may be more or less. Modifying sql also needs to modify the code, so the system is not easy to maintain.

4. There is hard coding (query column name) for the result set parsing. The change of sql leads to the change of parsing code, and the system is difficult to maintain. It is more convenient to parse if the database records can be encapsulated into pojo objects.

4. Build MyBatis development environment

4.1 create Maven project

<dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

4.2 writing entity classes

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

4.3 writing persistence layer interface

public interface IUserDao {
    /**
     * Query all
     * @return
     */
    List<User> findAll();
}

4.4 write the mapping file of persistence layer interface

Creation location: must be in the same package as the persistence layer interface.
Name: the file name must be named with the name of the persistence layer interface, and the extension is xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baidu.dao.IUserDao">
<!--    Configure query all-->
    <select id="findAll" resultType="com.company.domain.User">
        select * from user
    </select>
</mapper>

4.5 writing SqlMapConfig configuration file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--Configuration environment-->
    <environments default="mysql">
        <!--to configure mysql environment-->
        <environment id="mysql">
            <!--Configure transaction type-->
            <transactionManager type="JDBC"></transactionManager>
            <!--Configure data source (connection pool)-->
            <dataSource type="POOLED">
                <!--Four basic information for configuring and connecting to the database-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatisdb"/>
                <property name="username" value="root"/>
                <property name="password" value="Root@2020"/>
            </dataSource>
        </environment>
    </environments>
    <!--Specify the location of the mapping profile-->
    <mappers>
        <mapper resource="com/company/dao/IUserDao.xml"/>
    </mappers>
</configuration>

4.6 writing test classes

public class MybatisTest {
    public static void main(String[] args) throws Exception {
        //1. Read the configuration file
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

        //2. Create SqlSessionFactory factory
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);

        //3. Use factory to produce SqlSession object
        SqlSession session = factory.openSession();

        //4. Use SqlSession to create the proxy object of DAO
        IUserDao userDao = session.getMapper(IUserDao.class);

        //5. Use the proxy object to execute the method
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }

        //6. Release resources
        session.close();
        in.close();
    }
}

5 three common design modes

5.1 factory mode

Factory Pattern is one of the most commonly used design patterns in Java. This type of design pattern is a creation pattern, which provides the best way to create objects.

In factory mode, when creating objects, we do not expose the creation logic to the client, and point to the newly created objects by using a common interface.

Intention: define an interface for creating objects, and let its subclasses decide which factory class to instantiate. The factory mode delays the creation process to the subclasses.

Main solution: mainly solve the problem of interface selection.

When to use: when we explicitly plan to create different instances under different conditions.

How to solve it: let its subclasses implement the factory interface and return an abstract product.

Key code: the creation process is executed in its subclass.

5.2 agent mode

In Proxy Pattern, one class represents the functions of another class. This type of design pattern belongs to structural pattern.

In proxy mode, we create objects with existing objects to provide functional interfaces to the outside world.

Intent: to provide a proxy for other objects to control access to this object.

It mainly solves the problems caused by directly accessing objects. For example, the object to be accessed is on a remote machine. In the object-oriented system, some objects will bring a lot of trouble to users or system structure due to some reasons (such as high cost of object creation, or some operations need security control, or need out of process access). We can add an access layer to this object when accessing this object.

When to use: I want to do some control when accessing a class.

How to solve: add an intermediate layer.

Key code: combination of implementation and proxy class.

5.3 builder mode

Builder Pattern uses multiple simple objects to build a complex object step by step. This type of design pattern is a creation pattern, which provides the best way to create objects.

A Builder class will construct the final object step by step. The Builder class is independent of other objects.

Intent: separate a complex build from its representation so that the same build process can create different representations.

Main solution: it mainly solves the problem of creating "a complex object" in software system, which is usually composed of sub objects of various parts with certain algorithms; Due to the change of requirements, each part of this complex object often faces drastic changes, but the algorithm that combines them is relatively stable.

When to use: when some basic components will not change, but their combination often changes.

How to solve: separate change from invariance.

Key code: Builder: create and provide instances, director: manage the dependencies of the built instances.

6 customize MyBatis framework

6.1 creating Maven project

<dependencies>
<!--        <dependency>-->
<!--            <groupId>org.mybatis</groupId>-->
<!--            <artifactId>mybatis</artifactId>-->
<!--            <version>3.4.5</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>
    </dependencies>

6.2 introduction of tools

public class XMLConfigBuilder {


    /**
     * Fill in the required contents of the default session configuration file
     * Technology used:
     * dom4j+xpath
     */
    public static Configuration loadConfiguration(InputStream config) {
        try {
            //Define the configuration object that encapsulates connection information (configuration object of mybatis)
            Configuration cfg = new Configuration();

            //1. Get SAXReader object
            SAXReader reader = new SAXReader();
            //2. Obtain the Document object according to the byte input stream
            Document document = reader.read(config);
            //3. Get the root node
            Element root = document.getRootElement();
            //4. Use the method of selecting the specified node in xpath to obtain all property nodes
            List<Element> propertyElements = root.selectNodes("//property");
            //5. Traverse nodes
            for (Element propertyElement : propertyElements) {
                //Determine which part of the information the node is connected to the database
                //Get the value of the name attribute
                String name = propertyElement.attributeValue("name");
                if ("driver".equals(name)) {
                    //Represent drive
                    //Gets the value of the property tag value property
                    String driver = propertyElement.attributeValue("value");
                    cfg.setDriver(driver);
                }
                if ("url".equals(name)) {
                    //Represents a connection string
                    //Gets the value of the property tag value property
                    String url = propertyElement.attributeValue("value");
                    cfg.setUrl(url);
                }
                if ("username".equals(name)) {
                    //Indicates the user name
                    //Get the value of the property tag value property
                    String username = propertyElement.attributeValue("value");
                    cfg.setUsername(username);
                }
                if ("password".equals(name)) {
                    //Indicates the password
                    //Gets the value of the property tag value property
                    String password = propertyElement.attributeValue("value");
                    cfg.setPassword(password);
                }
            }
            //Take out all mapper tags in mappers and judge whether they use resource or class attribute
            List<Element> mapperElements = root.selectNodes("//mappers/mapper");
            //Traversal set
            for (Element mapperElement : mapperElements) {
                //Determine which attribute mapperElement uses
                Attribute attribute = mapperElement.attribute("resource");
                if (attribute != null) {
                    System.out.println("Using XML");
                    //Indicates that there is a resource attribute, using XML
                    //Get the value of the attribute
                    String mapperPath = attribute.getValue();//Get the value of attribute "com/itheima/dao/IUserDao.xml"
                    //Get the content of the mapping configuration file and package it into a map
                    Map<String, Mapper> mappers = loadMapperConfiguration(mapperPath);
                    //Assign a value to mappers in configuration
                    cfg.setMappers(mappers);
                } else {
                    System.out.println("Annotations are used");
                    //Indicates that there is no resource attribute, but the annotation is used
                    //Gets the value of the class property
                    String daoClassPath = mapperElement.attributeValue("class");
                    //Obtain the necessary information of encapsulation according to daoClassPath
                    Map<String, Mapper> mappers = loadMapperAnnotation(daoClassPath);
                    //Assign a value to mappers in configuration
                    cfg.setMappers(mappers);
                }
            }
            //Return to Configuration
            return cfg;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                config.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * According to the passed in parameters, the XML is parsed and encapsulated into the Map
     *
     * @param mapperPath Location of the mapping profile
     * @return map It contains the unique identifier obtained (key is composed of dao's fully qualified class name and method name)
     * And the necessary information required for execution (value is a Mapper object, which stores the executed SQL statement and the fully qualified class name of the entity class to be encapsulated)
     */
    private static Map<String, Mapper> loadMapperConfiguration(String mapperPath) throws IOException {
        InputStream in = null;
        try {
            //Define return value object
            Map<String, Mapper> mappers = new HashMap<String, Mapper>();
            //1. Get byte input stream according to path
            in = Resources.getResourceAsStream(mapperPath);
            //2. Obtain the Document object according to the byte input stream
            SAXReader reader = new SAXReader();
            Document document = reader.read(in);
            //3. Get the root node
            Element root = document.getRootElement();
            //4. Get the namespace attribute value of the root node
            String namespace = root.attributeValue("namespace");//Is the part of the key in the map
            //5. Get all the select nodes
            List<Element> selectElements = root.selectNodes("//select");
            //6. Traverse the select node set
            for (Element selectElement : selectElements) {
                //Take out the value of the id attribute to form the key part of the map
                String id = selectElement.attributeValue("id");
                //Extract the value of the resultType attribute to form the value part of the map
                String resultType = selectElement.attributeValue("resultType");
                //Take out the text content to form the part of value in the map
                String queryString = selectElement.getText();
                //Create Key
                String key = namespace + "." + id;
                //Create Value
                Mapper mapper = new Mapper();
                mapper.setQueryString(queryString);
                mapper.setResultType(resultType);
                //Store key and value in mappers
                mappers.put(key, mapper);
            }
            return mappers;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            in.close();
        }
    }

    /**
     * According to the parameters passed in, get all the methods marked by the select annotation in dao.
     * According to the method name and class name, as well as the value of the annotated value attribute on the method, the necessary information of Mapper is composed
     *
     * @param daoClassPath
     * @return
     */
    private static Map<String, Mapper> loadMapperAnnotation(String daoClassPath) throws Exception {
        //Define return value object
        Map<String, Mapper> mappers = new HashMap<String, Mapper>();

        //1. Get the bytecode object of dao interface
        Class daoClass = Class.forName(daoClassPath);
        //2. Get the method array in dao interface
        Method[] methods = daoClass.getMethods();
        //3. Traverse the Method array
        for (Method method : methods) {
            //Take out each method and judge whether there is a select annotation
            boolean isAnnotated = method.isAnnotationPresent(Select.class);
            if (isAnnotated) {
                //Create Mapper object
                Mapper mapper = new Mapper();
                //Take out the value attribute value of the annotation
                Select selectAnno = method.getAnnotation(Select.class);
                String queryString = selectAnno.value();
                mapper.setQueryString(queryString);
                //To get the return value of the current method, it is also required to have generic information
                Type type = method.getGenericReturnType();//List<User>
                //Judge whether type is a parameterized type
                if (type instanceof ParameterizedType) {
                    //Strong rotation
                    ParameterizedType ptype = (ParameterizedType) type;
                    //Get the actual type parameter in the parameterized type
                    Type[] types = ptype.getActualTypeArguments();
                    //Take out the first one
                    Class domainClass = (Class) types[0];
                    //Get the class name of domainClass
                    String resultType = domainClass.getName();
                    //Assign a value to Mapper
                    mapper.setResultType(resultType);
                }
                //Assembly key information
                //Gets the name of the method
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                String key = className + "." + methodName;
                //Assign value to map
                mappers.put(key, mapper);
            }
        }
        return mappers;
    }
}

public class Executor {

    public <E> List<E> selectList(Mapper mapper, Connection conn) {
        PreparedStatement pstm = null;
        ResultSet rs = null;
        try {
            //1. Take out the data in mapper
            String queryString = mapper.getQueryString();//select * from user
            String resultType = mapper.getResultType();//com.itheima.domain.User
            Class domainClass = Class.forName(resultType);
            //2. Get PreparedStatement object
            pstm = conn.prepareStatement(queryString);
            //3. Execute SQL statement to obtain result set
            rs = pstm.executeQuery();
            //4. Encapsulate the result set
            List<E> list = new ArrayList<E>();//Define return value
            while(rs.next()) {
                //Instantiate the entity class object to encapsulate
                E obj = (E)domainClass.newInstance();

                //Get the meta information of the result set: ResultSetMetaData
                ResultSetMetaData rsmd = rs.getMetaData();
                //Total number of columns taken out
                int columnCount = rsmd.getColumnCount();
                //Total number of columns traversed
                for (int i = 1; i <= columnCount; i++) {
                    //Get the name of each column. The sequence number of the column name starts from 1
                    String columnName = rsmd.getColumnName(i);
                    //Get the value of each column according to the column name
                    Object columnValue = rs.getObject(columnName);
                    //Assign value to obj: use Java introspection mechanism (realize property encapsulation with the help of PropertyDescriptor)
                    PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//Requirement: the attribute of the entity class and the column name of the database table should be the same
                    //Get its write method
                    Method writeMethod = pd.getWriteMethod();
                    //Assign the value of the obtained column to the object
                    writeMethod.invoke(obj,columnValue);
                }
                //Add the valued object to the collection
                list.add(obj);
            }
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            release(pstm,rs);
        }
    }


    private void release(PreparedStatement pstm,ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }

        if(pstm != null){
            try {
                pstm.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

public class DataSourceUtil {
    public static Connection getConnection(Configuration cfg) {
        try {
            Class.forName(cfg.getDriver());
            return DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

6.3 writing and reading configuration file classes

/**
 * Read configuration file using classloader
 */
public class Resources {
    /**
     * Get a byte input stream according to the passed in parameters
     *
     * @param filepath
     * @return
     */
    public static InputStream getResourceAsStream(String filepath) {
        return Resources.class.getClassLoader().getResourceAsStream(filepath);
    }
}

6.4 writing Mapper classes

/**
 * The fully qualified class name used to encapsulate the executed SQL statement and result type
 */
public class Mapper {
    private String queryString;//SQL
    private String resultType;//Fully qualified class name of entity class

    public String getQueryString() {
        return queryString;
    }

    public void setQueryString(String queryString) {
        this.queryString = queryString;
    }

    public String getResultType() {
        return resultType;
    }

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

6.5 write Configuration class

public class Configuration {
    private String driver;//drive
    private String url;//address
    private String username;//user name
    private String password;//password
    //Map < unique ID, mapper > is used to save the sql ID and sql statement in the mapping file
    private Map<String, Mapper> mappers;

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

    public void setMappers(Map<String, Mapper> mappers) {
        //You need to add here
        this.mappers = mappers;
    }

    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;
    }
}

6.6 write builder class

/**
 * Used to create a SqlSessionFactory object
 */
public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(InputStream in) {
        DefaultSqlSessionFactory factory = new DefaultSqlSessionFactory();
        factory.setConfig(in);
        return factory;
    }
}

6.7 write SqlSessionFactory interface and implementation class

public interface SqlSessionFactory {
    SqlSession openSession();
}
/**
 * SqlSessionFactory Implementation class of interface
 */
public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private InputStream config = null;

    public void setConfig(InputStream config) {
        this.config = config;
    }

    /**
     * Used to create a new operation database object
     *
     * @return
     */
    @Override
    public SqlSession openSession() {
        Configuration cfg = XMLConfigBuilder.loadConfiguration(config);
        return new DefaultSqlSession(cfg);
    }
}

6.8 write SqlSession interface and implementation class

/**
 * Customize the core classes that Mybatis interacts with the database
 * You can create proxy objects for dao interfaces
 */
public interface SqlSession {
    /**
     * Create a proxy object based on the parameters
     * @param daoInterfaceClass dao Interface bytecode
     * @param <T>
     * @return
     */
    <T> T getMapper(Class<T> daoInterfaceClass);

    /**
     * Release resources
     */
    void close();
}

/**
 * SqlSession Implementation class
 */
public class DefaultSqlSession implements SqlSession {
    private Configuration cfg;
    private Connection conn;

    public DefaultSqlSession(Configuration cfg) {
        this.cfg = cfg;
        this.conn = DataSourceUtil.getConnection(cfg);
    }

    /**
     * Used to create proxy objects
     *
     * @param daoInterfaceClass dao Interface bytecode
     * @param <T>
     * @return
     */
    @Override
    public <T> T getMapper(Class<T> daoInterfaceClass) {
        return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),
                new Class[]{daoInterfaceClass},
                new MapperProxy(cfg.getMappers(), conn));
    }

    /**
     * Used to free up resources
     */
    @Override
    public void close() {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

    //Query all methods
    public <E> List<E> selectList(String statement) {
        Mapper mapper = cfg.getMappers().get(statement);
        return new Executor().selectList(mapper, conn);
    }
}

6.9 write classes for creating Dao interface proxy objects

public class MapperProxy implements InvocationHandler {
    //The key of Map is fully qualified class name + method name
    private Map<String, Mapper> mappers;
    private Connection conn;

    public MapperProxy(Map<String, Mapper> mappers, Connection conn) {
        this.mappers = mappers;
        this.conn = conn;
    }

    /**
     * It is used to enhance the method by calling the SelectList method
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1. Get method name
        String methodName = method.getName();

        //2. Get the name of the class where the method is located
        String className = method.getDeclaringClass().getName();

        //3. Combination key
        String key = className + "." + methodName;

        //4. Get mapper object in Mappers
        Mapper mapper = mappers.get(key);

        //5. Judge whether there is a mapper
        if (mapper == null) {
            throw new IllegalArgumentException("The parameter passed in is incorrect");
        }
        Executor executor = new Executor();
        //6. Call the tool class to query all
        return executor.selectList(mapper,conn);
    }
}

Tags: Java MySQL Mybatis Spring

Posted by dearsina on Thu, 21 Apr 2022 03:42:25 +0300