[MyBatis in depth analysis] Application Analysis and best practice

MyBatis programming development

MyBatis programming development steps
  1. MyBatis and MySQL Jar package dependency
  2. Global configuration file mybatis-config.xml
  3. Mapper.xml
  4. Mapper interface
Programming pseudocode
    @Test
    public void show() {
        SqlSession session = null;
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            session = sqlSessionFactory.openSession();
            Student student = session.selectOne("com.fly.crud.mapper.StudentMapper.selectDataById", 1);
            log.info("student jsonStr is {}", JSON.toJSONString(student));
        } catch (Exception e) {
            log.error("MyBatis Programming exception.", e);
        } finally {
            if (Objects.nonNull(session)) {
                session.close();
            }
        }
    }
MyBatis programming core object
  1. SqlSessionFactoryBuilder
  2. SqlSessionFactory
  3. SqlSession
  4. Mapper
The life cycle of MyBatis programming core objects
object life cycle
SqlSessionFactoryBuilder method local
SqlSessionFactory(Singleton) application level
SqlSession Requests and actions
Mapper Method local
MyBatis core configuration
Label Effect
configuration MyBatis root tag
properties mybatis global properties
settings mybatis settings
typeAliases mybatis type alias
typeHadlers mybatis type processor
objectFactory mybatis object Engineering
plugins mybatis plug-in
environments mybatis environment variable
databaseIdProvider Manufacturer identification of mybatis database
mappers mybatis mapper
Default source code interpretation of settings
private void settingsElement(Properties props) {
    // Specifies how MyBatis should automatically map columns to fields or properties. NONE means auto mapping is turned off; PARTIAL only maps fields that do not have nested result mapping defined. FULL automatically maps any complex result set (nested or not)
    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    /**
     * Specifies the behavior of discovering unknown columns (or unknown property types) for automatic mapping targets.
     *  NONE: No reaction
     *  WARNING: Output warning log ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior 'log level must be set to WARN)
     *  FAILING: Mapping failed (sqlsessionexception thrown)
     */
    configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    // Globally turn on or off any cache configured in all mapper profiles
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    // Specifies the proxy tool Mybatis uses to create the lazy load object.
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    // Global switch to delay loading. When on, all associated objects delay loading. The switch state of the item can be overridden by setting the fetchType property in a specific association.
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    // When on, calls to either method load all of the object's lazy load properties. Otherwise, each delayed load property is loaded on demand (refer to lazyLoadTriggerMethods).
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    // Whether to allow single statement to return multiple result sets (need database driver support)
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    // Use column labels instead of column names. The actual performance depends on the database driver. For details, please refer to the relevant documents of database driver or observe through comparative test
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    // Allowing JDBC to support automatic primary key generation requires database driver support. If set to true, automatic primary key generation is enforced. Although some database drivers do not support this feature, they still work (such as Derby)
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    // Configure the default actuator. SIMPLE is a common executor; REUSE executors REUSE prepared statement; BATCH executors not only REUSE statements, but also perform BATCH updates.
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    // Sets the timeout, which determines the number of seconds the database driver waits for a response from the database.
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    // Set a recommended value for the fetchSize of the driven result set. This parameter can only be overridden in query settings.
    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
    // Specifies the default scrolling policy for the statement. (added in 3.5.2)
    configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
    // Whether to enable automatic mapping of hump naming, that is, mapping from classic database column name a column to classic Java attribute name aColumn.
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    // Whether to allow paging (RowBounds) in nested statements. Set to false if allowed.
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    /** MyBatis Local Cache is used to prevent circular reference and accelerate repeated nested queries.
     * The default value is SESSION, which caches all queries executed in a SESSION.
     * If the value is set to STATEMENT, the local cache will only be used to execute statements, and different queries for the same SqlSession will not be cached.
     */
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    // The default JDBC type for NULL when no specific JDBC type is specified for the parameter. Some database drivers need to specify the JDBC type of the column. In most cases, you can use the general type directly, such as NULL, VARCHAR or OTHER.
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    // Specifies which methods of the object trigger a lazy load
    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    // Whether the ResultHandler is allowed in nested statements. Set to false if allowed.
    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    // Specifies the default scripting language for dynamic SQL generation.
    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    // Specifies the default TypeHandler used by Enum. (added in 3.4.5)
    configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
    // Specifies whether to call the setter (put) method of the mapping object when the value in the result set is null, which is useful when initialization depends on the Map.keySet() or null value. Note that basic types (int, boolean, etc.) cannot be set to null.
    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    // The name in the method signature is allowed as the statement parameter name. To use this feature, your project must be compiled in Java 8 with the - parameters option. (added in 3.4.1)
    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    // MyBatis returns null by default when all columns of the returned row are empty. When this setting is turned on, MyBatis returns an empty instance. Note that it also applies to nested result sets, such as collections or associations. (added in 3.4.2)
    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    // Specifies the prefix MyBatis added to the log name.
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    /**
     * Specifies a class that provides a Configuration instance. The returned Configuration instance is used to load the delayed load property value of the deserialized object.
     * This class must contain a method signed static Configuration getConfiguration(). (added in 3.2.3)
     */
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
  }

MyBatis best practices

Bulk insert pseudo code
        SqlSession sqlSession = null;
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
            List<Student> studentList = new ArrayList<>(16);
            for (int i = 0; i < 5; i++) {
                int hashCodeV = UUID.randomUUID().toString().hashCode();
                hashCodeV = hashCodeV>0?hashCodeV:-hashCodeV;
                Student student = new Student("Teng Fei"+i,String.valueOf(hashCodeV));
                studentList.add(student);
            }
            mapper.insertDataBatch(studentList);
            sqlSession.commit();
        } catch (IOException e) {
            log.error("StudentMapper insertDataBatch fail.", e);
        } finally {
            if (Objects.nonNull(sqlSession)) {
                sqlSession.close();
            }
        }
Batch query pseudo code
        SqlSession sqlSession = null;
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
            List<Integer> ids = Arrays.asList(1,12,13,14,15);
            List<Student> students = mapper.selectDataByIdBatch(ids);
            log.info("students jsonStr is {}",JSON.toJSONString(students));
        } catch (IOException e) {
            log.error("StudentMapper insertDataBatch fail.", e);
        } finally {
            if (Objects.nonNull(sqlSession)) {
                sqlSession.close();
            }
        }
Delay loading pseudo code
        SqlSession sqlSession = null;
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
            StaffMapper mapper = sqlSession.getMapper(StaffMapper.class);
            StaffAndDepartment staffAndDepartment = mapper.findStaffWithDepartmentQuery(1);
            System.out.println("-----------:"+staffAndDepartment.getClass());
            // If delay loading is enabled, SQL will be issued only when it is used
            // equals,clone,hashCode,toString will also trigger delayed loading
            // System.out.println("------ call toString method:" + blog);
//            System.out.println("-----------getAuthor:"+staffAndDepartment.toString());
            // If aggressiveLazyLoading = true, the load will also be triggered, otherwise it will not
            System.out.println("-----------getName:"+staffAndDepartment.getName());
        } catch (IOException e) {
            log.error("System exception.", e);
        } finally {
            if (Objects.nonNull(sqlSession)) {
                sqlSession.close();
            }
        }
Page turning
  • Difference between logical page turning and physical page turning
    • Logical paging - false paging: load all the data into memory. According to the offset and the number of entries in rowBound, remove all the previous offsets and only the part of the specified amount after the offset starts;
    • Physical paging - true paging: hardcode paging information into sql statements, and directly query the required part of data in the database
Logical page turning pseudo code
        SqlSession sqlSession = null;
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
            StaffMapper mapper = sqlSession.getMapper(StaffMapper.class);
            int start = 0; // offset
            int pageSize = 5; // limit
            RowBounds rowBounds = new RowBounds(start, pageSize);
            List<Staff> staffs = mapper.findStaffAll(rowBounds);
            log.info("staffs jsonStr is {}", JSON.toJSONString(staffs));
        } catch (IOException e) {
            log.error("System exception.", e);
        } finally {
            if (Objects.nonNull(sqlSession)) {
                sqlSession.close();
            }
        }
Physical page turning pseudo code
/**
* mapper To configure
**/
@Select("select name from staff limit #{start},#{end}")
    List<Staff> findStaffPhysical(@Param("start") int start, @Param("end") int end);

SqlSession sqlSession = null;
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
            StaffMapper mapper = sqlSession.getMapper(StaffMapper.class);
            List<Staff> staffs = mapper.findStaffPhysical(0, 5);
            log.info("staffs jsonStr is {}", JSON.toJSONString(staffs));
        } catch (IOException e) {
            log.error("System exception.", e);
        } finally {
            if (Objects.nonNull(sqlSession)) {
                sqlSession.close();
            }
        }

The chapter of application analysis and best practice is summarized here. The next chapter is about MyBatis architecture and working principle. I'd like to know what happened next and listen to the next chapter.

Tags: Java Mybatis Database Session xml

Posted on Sun, 26 Apr 2020 03:55:02 -0700 by ragy