Blog: bugstack.cn

Precipitation, share, grow, let yourself and others can gain something! 😄

One, foreword

How to design complex systems?

We can call a large framework like Spring, Mybatis, Dubbo, or some of the more core projects within a company complex systems. Such a project is no longer a toy project in the hands of beginners in programming. There is no SO-CALLED CRUD, but more often, it is to design the hierarchical structure of the system and realize the aggregation logic function, and then implement and invoke it through layer upon layer transformation.

This is very uncomfortable for many small size farmers who have just entered the road. They do not know where to start, but they think they can eat fat. In fact, this is not realistic, because there are too many contents in the framework of these complex systems that you have not yet understood and become familiar with. The more you try, the more uncomfortable it will be.

In fact, to solve this kind of complex project problems, the core is to reduce the main problem point, the specific means include: divide and conquer, abstraction and knowledge. Using design patterns and design principles and other related knowledge, the problem space is reasonably cut into a number of sub-problems, the smaller the problem is easier to understand and deal with. Just as you can take a lot of content and make it into a single case, you end up aggregating it.

Second, the target

In the previous chapter we took a look at how to generate a mapper proxy for an interface class and perform some user calls to interface methods in the proxy. Although we’ve seen a core logical approach, there’s still a bit of slash-and-burn in use, including the need to code to tell MapperProxyFactory which interface to proxy on, and the need to write a fake SqlSession to handle the return of the actual interface call.

Therefore, combining these two problems, we will provide registration machine processing for mapper registration in this chapter, so that users can provide a package path when using to complete scanning and registration. At the same time, we need to normalize SqlSession, so that it can wrap our mapper agent and method call, and build a life cycle model structure for subsequent content addition.

Three, the design

Since we want to associate the DAO interface for database operations across the project package with the Mapper Mapper, we need to wrap a map-completing registry class that can scan the package path.

Of course, we need to improve on the simplified SqlSession from the previous chapter, which defines the database processing interface and the operations that fetch the Mapper object and hand it over to the Mapper agent class for use. This part is the improvement of the previous chapter

With SqlSession, you can think of it as a functional service, and with a functional service, you need to provide a factory for the functional service to provide such services externally. For example, the common operation in Mybatis is to open a SqlSession. The whole design can be shown in Figure 3-1

  • To provide the mapper proxy class for the wrapper interface, complete the mapper registry machineMapperRegistry, automatically scans the interfaces under the package and saves all the proxy classes mapped by each interface class into the HashMap cache of the mapper agent.
  • And SqlSession, SqlSessionFactory is registered in this mapping agent last layer using standard definition and external services provided by the package, easy for users to use.We treat the user as the userAfter such encapsulation, it will be more convenient for us to continue to expand the functions of the framework. We also hope that you can have some thoughts on this design structure in the process of learning, which can help you solve the domain service packaging in the process of developing some business functions.

Four, implementation,

1. Engineering structure

mybatis-step-02└ ─ ─ the SRC ├ ─ ─ the main │ └ ─ ─ Java │ └ ─ ─ cn. Bugstack. Mybatis │ ├ ─ ─ binding │ │ ├ ─ ─ MapperProxy. Java │ │ ├ ─ ─ ├─ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ DefaultSqlSessionFactory. Java │ ├ ─ ─ SqlSession. Java │ └ ─ ─ SqlSessionFactory. Java └ ─ ─ the test └ ─ ─ Java └ ─ ─ Cn. Bugstack. Mybatis. Test. The dao ├ ─ ─ dao │ ├ ─ ─ ISchoolDao. Java │ └ ─ ─ IUserDao. Java └ ─ ─ ApiTest. JavaCopy the code

Project source: t.zsxq.com/bmqNFQ7

The mapper standard defines the implementation relationship, as shown in Figure 3-2

  • MapperRegistry provides package path scanning and mapper proxy class registration machine services to complete the proxy class registration of interface objects.
  • SqlSession and DefaultSqlSession are used to define operations for executing THE SQL standard, obtaining the mapper, and managing future transactions. Basically, we usually use the API interface of Mybatis from the method defined by this interface class.
  • SqlSessionFactory is a simple factory pattern used to provide THE SqlSession service, masking the creation details and delaying the creation process.

2. Register the mapping machine

Source see: cn bugstack. Mybatis. Binding. The MapperRegistry

public class MapperRegistry {

    /** * Adds the added mapper agent to the HashMap */
    private finalMap<Class<? >, MapperProxyFactory<? >> knownMappers =new HashMap<>();

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new RuntimeException("Error getting mapper instance. Cause: "+ e, e); }}public <T> void addMapper(Class<T> type) {
        /* Mapper must be an interface */
        if (type.isInterface()) {
            if (hasMapper(type)) {
                // If it is added repeatedly, an error is reported
                throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");
            }
            // Register the mapper agent factory
            knownMappers.put(type, newMapperProxyFactory<>(type)); }}public void addMappers(String packageName) { Set<Class<? >> mapperSet = ClassScanner.scanPackage(packageName);for(Class<? > mapperClass : mapperSet) { addMapper(mapperClass); }}}Copy the code
  • The core of the MapperRegistry registry class is that it providesClassScanner.scanPackageScan the package path, calladdMapperMethod to create an interface classMapperProxyFactoryMapper proxys the class and writes to knownMappers’ HashMap cache.
  • In addition, this class also provides the corresponding getMapper to obtain the Mapper proxy class method, in fact, this step is the manual instantiation process of the previous chapter, more convenient to use when obtaining Mapper in DefaultSqlSession.

3. SqlSession standard definition and implementation

Source see: cn bugstack. Mybatis. Session. The SqlSession

public interface SqlSession {

    /** * Retrieve a single row mapped from the statement key@param<T> The returned Object Type The object type after encapsulation *@param statement sqlID
     * @returnMapped Object */
    <T> T selectOne(String statement);

    /** * Retrieve a single row mapped from the statement key and parameter However, this method allows you to pass some parameters to SQL * in real use, this parameter is passed poJO, Map or ImmutableMap * *@param <T>       the returned object type
     * @param statement Unique identifier matching the statement to use.
     * @param parameter A parameter object to pass to the statement.
     * @return Mapped object
     */
    <T> T selectOne(String statement, Object parameter);

    /** * Retrieves a mapper. * This clever use of generics makes type safe **@param <T>  the mapper type
     * @param type Mapper interface class
     * @return a mapper bound to this SqlSession
     */
    <T> T getMapper(Class<T> type);

}
Copy the code
  • Standard interfaces for executing SQL, retrieving mapper objects, and managing subsequent transaction operations are defined in SqlSession.
  • Currently, only selectOne is provided for database operations in this interface, and other methods will be defined in the future.

Source see: cn bugstack. Mybatis. Session. The defaults

public class DefaultSqlSession implements SqlSession {

    /** * mapper register machine */
    private MapperRegistry mapperRegistry;

    @Override
    public <T> T selectOne(String statement, Object parameter) {
        return (T) ("You're being represented!" + "Method:" + statement + "Entry parameter:" + parameter);
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return mapperRegistry.getMapper(type, this); }}Copy the code
  • DefaultSqlSession implementation class is used to implement SqlSession interface.
  • The mapper object is obtained in the getMapper method through the MapperRegistry class, which is subsequently replaced by the configuration class.
  • In selectOne, it is a simple content return that is not currently associated with the database, which is implemented gradually in our incremental development process.

4. SqlSessionFactory definition and implementation

Source see: cn bugstack. Mybatis. Session. SqlSessionFactory

public interface SqlSessionFactory {

    /** * Open a session *@return SqlSession
     */
   SqlSession openSession(a);

}
Copy the code
  • This is essentially the definition of a simple factory, the ability to provide interface implementation classes in the factory, which is the ability to start sqlSessions provided in the SqlSessionFactory factory.

Source see: cn bugstack. Mybatis. Session. Defaults. DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final MapperRegistry mapperRegistry;

    public DefaultSqlSessionFactory(MapperRegistry mapperRegistry) {
        this.mapperRegistry = mapperRegistry;
    }

    @Override
    public SqlSession openSession(a) {
        return newDefaultSqlSession(mapperRegistry); }}Copy the code
  • The default simple factory implementation handles the creation of DefaultSqlSession when SqlSession is started and the passing of mapperRegistry so that the mapper object for each proxy class can be retrieved when SqlSession is used.

Five, the test

1. Prepare

More than two Dao interfaces are provided in the same package path:

public interface ISchoolDao {

    String querySchoolName(String uId);

}

public interface IUserDao {

    String queryUserName(String uId);

    Integer queryUserAge(String uId);

}
Copy the code

Unit testing

@Test
public void test_MapperProxyFactory(a) {
    // 1. Register Mapper
    MapperRegistry registry = new MapperRegistry();
    registry.addMappers("cn.bugstack.mybatis.test.dao");
    
    // 2. Obtain the Session from the SqlSession factory
    SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(registry);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    
    // 3. Get the mapper object
    IUserDao userDao = sqlSession.getMapper(IUserDao.class);
    
    // 4. Test validation
    String res = userDao.queryUserName("10001");
    logger.info("Test result: {}", res);
}
Copy the code
  • Registering the mapper agent object by scanning the package path with the registry in the unit test and passing the registry to the SqlSessionFactory completes a linking process.
  • Then the implementation class corresponding to DAO type is obtained through SqlSession and method verification is carried out.

The test results

22:43:23.254[the main] INFO cn. Bugstack. Mybatis. Test. The ApiTest - test results: you have been acting! QueryUserName input parameter: [ljava.lang.Object;@50cbc42f Process finished with exit code0
Copy the code
  • Through the test, we can see that we have completed the registration and use of proxy class in a handwritten ORM framework with Mybatis shadow.

Six, summarized

  • First of all, it is necessary to understand the packaging of factory mode to the specific functional structure from the design structure, shield the process details, limit the context relations, and reduce the coupling of external use.
  • As you can see from this process, the factory implementation class using SqlSessionFactory wraps the standard definition implementation class of SqlSession, and SqlSession registers and uses the mapper object.
  • There are several important points to pay attention to in this chapter, including: mapper, proxy class, registry, interface standard, factory pattern, context. These engineering development skills are a very important part in the process of writing Mybatis, understanding and familiarity can be better used in their own business.