Ape logic, ape logic, ape logic

A programmer working with databases will soon face a difficult choice. MyBatis or JPA?

Many people say that the choice of technology should be based on demand, which is true. However, in addition to the demand, there is a very important link, that is the level of teammates. If you choose something more advanced, you’re just setting the whole team up.

JPA has a higher level of abstraction and simpler code to write, but it is anything but simple. Despite a lot of training, I’ve worked with teams that still use it like shit.

I threw away the JPA

When I think about it, there are several reasons why JPA doesn’t work on many teams.

  1. JPA is suitable for scenarios where the business model is fixed and for more stable requirements. However, the domestic demand style of fickle, the product manager of this kind of communication style design model, caused the flood of demand and uncertainty. JPA in this mode is crap.
  2. JPA has high technical requirements. No doubt, you may find it very simple to use at first. But as you get deeper into it, it’s a disaster. With all the conversions and caches, it’s confusing. And most fast food programmers don’t want to know that.
  3. Many programmers are very good at writing SQL, so many SQL statements are very fat and very long. Business is chaotic, multiple tables are associated, I have even seen hundreds of business tables associated complex business. As dbAs have no choice, there is usually SQL auditing. JPA SQL audit? Still a little weaker.

So, it’s not that JPA is bad, it’s just that it doesn’t fit in. In order to implement JPA in the company, you need to give me a stable product team, a great technical team.

So it’s logical and logical that most companies would rather write a bunch of repetitive, messy Mybaits code than try JPA.

So, our following article is to discuss MyBatis, to see how to write Mybaits exactly is elegant.

Why doesn’t MyBatis work

Good programmers are lazy. So a lot of people don’t want to design SQL for entities. JPA can generate SQL library tables directly according to Java entity code, which is very enviable for people using Mybatis.

To use MyBatis, do it backwards. You need to design the library table, and then generate a bunch of Java code and configuration files in reverse from the library table.

This code generator is called mybatis-generator.

But be warned. This generator generates code with four modes!! This is the hardest part for beginners. If you are new to MyBatis, it is highly recommended to focus only on the first mode below.

  • MyBatis3 is the usual way to generate domain classes, Example classes, mapper mapping files, etc. The information it generates is verbose and almost impossible to change. For self-written SQL in a project, it is common to write a second copy by hand rather than change the original file.
  • The simple code generation pattern for the pattern above, MyBatis3Simple, lacks something, but is concise. No experience with MyBatis, not recommended.
  • MyBatis3DynamicSql this is a dynamic SQL feature implemented through Builder mode, you also need to add additional JAR packages. When you add it, it’s actually a little bit similar to JPA. So why not just use JPA? So while this DSQL is the default generated behavior, it is not recommended.
  • MyBatis3Kotlin is no nonsense. This is to generate some configuration and code information for the Kotlin version.

So, the following is just code generation for MyBatis3 mode.

To use it, add its dependencies to pom.xml.

<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <optional>true</optional>
    <scope>test</scope>
    <version>1.4.0</version>
</dependency>
Copy the code

I personally like to use Java code to manipulate the code generation process, so here is the code that generates the code.

public class MBGTool {
    public static void main(String[] args) throws Exception {
        List<String> warnings = new ArrayList<>();
        boolean overwrite = true;
        InputStream configFile = MBGTool.class.getResourceAsStream("/generator/generatorConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null); }}Copy the code

From the code, we can see that we need to configure a generatorconfig.xml file that specifies how to generate the code file.

<! DOCTYPEgeneratorConfiguration PUBLIC
        "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <context id="simple" targetRuntime="MyBatis3">
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mbye"
                        userId="root"
                        password="root"
        />
        <javaModelGenerator targetPackage="com.github.javarunfast.mbye.domain" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <sqlMapGenerator targetPackage="com.github.javarunfast.mbye.mapper" targetProject="src/main/resources"/>
        <javaClientGenerator  type="XMLMAPPER"  targetPackage="com.github.javarunfast.mbye.mapper" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <table tableName="test"/>
    </context>
</generatorConfiguration>
Copy the code

After running our MBGTool file, we can generate MyBatis code.

What’s the most elegant way to write code

However, I’m not here to recommend that you use this model. Because, it generates a lot of useless files. If your project uses a code quality review tool like Sonar, you’ll find plenty of red flags and deadly coverage issues.

How to do?

After years of trial and error, I now recommend a very useful way to write it. I haven’t changed since I adopted it.

First, no code generators are needed

Data table design, as well as domain writing, all by hand. This way, our code can be migrated to JPA if necessary. This pattern also allows you to learn how Java data types correspond to SQL data types. When doing table design, the way to understand some of the principles behind.

Second, there is no need to write the mapping file

Generators do generate a lot of useless logic. For example, one of my tables does not need to provide the query all and delete actions at all, it still does by default.

In this minimalist mode, we write Mapper files by hand and declare only the interface methods we need.

@Mapper
public interface AccountBasicMapper {
    @Insert("AccountBasicMapper/insert.sql")
    void insert(@Param("r") AccountBasic record);
}
Copy the code

As you can see, there is an Insert annotation, we pass in a specific domain, and then we can write the specific SQL statement in the insert. SQL file in the AccountBasicMapper directory.

The following is an example of an SQL statement:

INSERT INTO account_basic(
    account_id,
    nick_name,
    password,
    sex,
    state,
    photo_url,
    created,
    modified,
    version
)VALUES (
    <@p name="r.accountId"/>.<@p name="r.nickName"/>.<@p name="r.password"/>.<@p name="r.sex"/>.<@p name="r.state"/>.<@p name="r.photoUrl"/>.<@p name="r.created"/>.<@p name="r.modified"/>.<@p name="r.version"/>
)
Copy the code

So what is this syntax? How does it know it’s configured this way? This requires the introduction of MyBatis scripting language configuration functionality. Here, we use the freemark template.

Don’t forget to add its dependencies.

<dependency>
	<groupId>org.mybatis.scripting</groupId>
	<artifactId>mybatis-freemarker</artifactId>
    <version>1.2.2</version>
</dependency>
Copy the code

Then, do the corresponding configuration in the YAML file and it is ok.

mybatis:
  check_config_location: false
  scripting-language-driver:
    freemarker:
      template-file:
        base-dir: mappers/
      path-provider:
        includes-package-path: false
        separate-directory-per-mapper: false
Copy the code

The pros and cons of this approach

I personally like this model very much. Because it has the following benefits:

  1. Write what you like with less code, simple and elegant.
  2. SQL is centralized, not scattered in code, XML, or annotations. Facilitate SQL audit by DBAs. Without the distraction of XML, SQL is much more concise.
  3. One DAO method and one SQL file with a single, controllable schema.
  4. The functional advantages of MyBatis can be fully developed and seamlessly integrated.

Of course, there are obvious drawbacks.

  1. Even if you change a parameter, you have to change a lot of SQL files.
  2. You need an SQL file for each method, even if it’s a stupid insert query method.

But I don’t see that as a problem. With an SQL file for each method, the code is much simpler to write. When problems occur, there is no logical trace to locate them in the concatenated SQL statement. I now, just need to get the corresponding method of SQL file, can change it, directly in the SQL terminal to perform debugging. In this way, SQL optimization becomes easier.

Of course, one person, one habit. I personally like this model and have found it works well in my team. In addition, programmers took the Dao interface more seriously in order to write less repetitive SQL code.

That’s probably a bonus.