Xiao Ming took over a project. There are many demands for organization management, personnel management and event management on the business side. These businesses include queries for terminal customers as well as management for internal managers. The complexity of requirements is not high, many forms are added, deleted, changed and checked, but there are too many demand points, Xiao Ming still has to work overtime as a brick code farmer. In fact, for this kind of scenario, there are many companies trying to use coding tools that break the boundaries of layers, such as PHP, Ruby/Rails, the first coding language in the universe, which is convenient and fast, but after the business is complicated, they have to slowly migrate to Java, the enterprise development language. There are also companies, began to try low code platform, through the simple pull pull to complete the development of background management interface, but now the low code development is really only competent for the above requirements of the increase, deletion, change and check.Copy the code

Is there a way to meet complex requirements in Java enterprise development? And can quickly complete the simple add, delete, change and check (there is a certain business logic), at the same time to ensure the uniformity and continuity of technology? Fluent-mybatis has completed the encapsulation of Mybatis, realized the use of streaming language in Java code, met the conditions setting, complex association, nesting, union, multi-database support, personalized extension and other convenient capabilities. Now comes forms-oriented add, delete, change, and review, which claims to implement the ability to block throat in a single swipe. Let's look at a simple example: We define a Spring Rest API as followsCopy the code
@RestController
@FormService(table = "student")
public interface StudentQueryApi {
    @PostMapping("/student")
    Student findStudentBy(@RequestBody StudentQuery student);
    
    @PostMapping("/listStudentBy")
    List<Student> listStudentBy(@RequestBody StudentQuery student);
}

@Data
@Accessors(chain = true)
public class StudentQuery implements Serializable {
    private String userName;

    @Entry(type = EntryType.LikeLeft)
    private String address;

    @Entry(type = Between)
    private Integer[] age;

    private Integer gender;
    /** * default positive */
    @Entry(type = EntryType.OrderBy, value = "userName")
    private boolean byUserName = true;
    /** * default reverse order */
    @Entry(type = EntryType.OrderBy, value = "age")
    private boolean byAge;
}

@Data
@Accessors(chain = true)
public class Student implements Serializable {

    private String userName;

    private String status;

    private String phone;

    @Entry("email")
    private String hisEmail;

    private Integer age;

    private String address;
}
Copy the code

Then add the API path to Spring Configuration using the @FormServicescan annotation, similar to myBatis @mapperscan

@formServicescan ({" Where you define the API package path "})
public class SpringConfig {
    // Configure your other beans
}
Copy the code

It’s easy. Even though you only define an interface and don’t write a line of implementation code, you have implemented a complete query logic. Query inputs include equality conditions, between conditions, like conditions and sorting Settings.

Start the SpringBoot application and let’s call it with the REST Client

Is it very simple, declaration is implementation, for a single table CRUD, product drawing prototype, development is basically completed, form-service function is also considered a low code implementation framework.

Let’s focus on the core concepts of form-services

  • @formService, defined on the interface class, indicates that the interface is a FormService interface
  • @FormServicescan, defined in the @Configuration class of Spring, is similar to @mapperscan of Mybatis. It is used to scan all @FormService interfaces
  • @formMethod, optional, defined on a Service method. If it is an insert or update method, it must be declared. If it is a query method, it is not declared
  • @entry, optional, defined on form fields. You need to explicitly define @entry in the following scenarios
    1. Condition fields, conditions are not equal, but greater than, less than, between, etc
    2. The form field name is inconsistent with the Entity field name
    3. Paging condition field (page number, number per page)
    4. Sort field
    5. Update the field

At the same time, in addition to the annotations that define the form-service, the following enhancements are required for interfaces and attributes

  • You can also use the Spring Rest annotation @RestController+@PostMapping to expose the interface directly as a REST API
  • Javax. validate annotation is used to verify the validity of the input parameter

Of course, all of the above functions are based on the fact that you have generated the Entity class according to the FluentMybatis specification. Fluent Mybatis code generation

Associated query and 1 + N problem solving

FormService can also solve the 1 to 1 and 1 to N associated query by simple declaration, and solve the 1+N query problem caused by the query list, all of which do not need you to code. Have a certain coding experience of the students, ask you so cool!!

  • When generating the Fluent Mybatis Entity class, define the correlation Relation of 1:1, 1:N through the @Relation annotation

Such as this

    @Tables(url = URL, username = "root", password = "password", srcDir = SrcDir, testDir = TestDir, basePack = BasePack + 2, gmtCreated = "gmt_created", gmtModified = "gmt_modified", logicDeleted = "is_deleted", tables = { @Table(value = {"student", "student_score"}, columns = @Column(value = "version", isLarge = true) ), relations = { @Relation(method = "findDeskMate", type = RelationType.OneWay_0_1, source = "student", target = "student", where = "id=desk_mate_id"), @Relation(source = "student", target = "student_score", type = RelationType.TwoWay_1_N, where = "id=student_id") })
    static class RelationDef1 {}Copy the code
1. The relationship between Student and deskmate is 1 vs 1Copy the code

The above code generates the Entity class as follows

@FluentMybatis( table = "student", schema = "fluent_mybatis" )
public class StudentEntity extends RichEntity {
    / /... Omitting property definitions
    
  /** * 1 to 1 associative query method */
  @RefMethod("deskMateId = id")
  public StudentEntity findDeskMate(a) {
    return super.invoke("findDeskMate".true);
  }

  /** * 1 Pair of associative query method */
  @RefMethod("studentId = id")
  public List<StudentScoreEntity> findStudentScoreList(a) {
    return super.invoke("findStudentScoreList".true); }}Copy the code
  • Define the associated object in the query interface return value, as shown below
@Data
@Accessors(chain = true)
public class Student {
    private Long id;

    private String userName;

    private String status;

    private String phone;

    @Entry("email")
    private String hisEmail;

    private Integer age;

    private String address;
    /** * findDeskMate method */
    private Student deskMate;
    /** * findStudentScoreList (scores); /** * findStudentScoreList (scores@EntryThe annotation explicitly specifies the correlation method */
    @Entry(value = "findStudentScoreList")
    private List<Score> scores;
}
Copy the code

Now let’s re-execute the listStudentBy test method and see what happens

@BeforeEach
void setup(a) {
    // Prepare data for 2 students
    ATM.dataMap.student.table(2)
        .env.values("test_env")
        .userName.values("li ming"."xiao qiang")
        .age.values(23.34)
        .email.values("xxx@test")
        .address.values("hangzhou binjiang")
        .deskMateId.values(2.1)
        .cleanAndInsert();
    Liming has two grades and Xiaoqiang has one
    ATM.dataMap.studentScore.table(3)
        .env.values("test_env")
        .studentId.values(1.1.2)
        .subject.values("yuwen"."english")
        .score.values(79.67.98)
        .cleanAndInsert();
}
    
@Test
void listEntity(a) {
    List<Student> students = service.listStudentBy(new StudentQuery()
        .setAddress("hangzhou")
        .setAge(new Integer[]{20.40}));
        
    // Validate list data
    want.object(students).eqDataMap(
        ATM.dataMap.student.entity(2)
            .userName.values("li ming"."xiao qiang")
            .age.values(23.34)
            .address.values("hangzhou binjiang")
            .kv("hisEmail"."xxx@test"));/* Your deskmate = yourself */
want.object(students.get(0)).eqReflect(students.get(1).getDeskMate(), EqMode.IGNORE_DEFAULTS);
    /* Verify the score list */
    want.object(students.get(0).getScores()).eqDataMap(new DataMap(2)
        .kv("score".79.67)
        .kv("subject"."yuwen"."english"));
    want.object(students.get(1).getScores()).eqDataMap(new DataMap(1)
        .kv("score".98)
        .kv("subject"."english"));
}
Copy the code

Then I looked at the SQL statement printed by the console. There were 3 SQL statements executed. 1. Select * from student where desk_mate_id IN (? ,?) Student_id IN (? Student_id IN (? ,?)

==> Preparing: SELECT `id`, `... FROM 'student' WHERE 'is_deleted' =? AND `env` = ? AND `address` LIKE ? AND `age` BETWEEN ? AND ? ORDER BY `user_name` ASC, `age` DESC ==> Parameters: false(Boolean), test_env(String), hangzhou%(String), 20(Integer), 40(Integer) <== Total: 2 ==> Preparing: SELECT `id`, `... FROM 'student' WHERE 'is_deleted' =? AND `env` = ? AND `desk_mate_id` IN (? ,?) ==> Parameters: false(Boolean), test_env(String), 1(Long), 2(Long) <== Total: 2 ==> Preparing: SELECT `id`, `... FROM 'student_score' WHERE 'is_deleted' =? AND `env` = ? AND `student_id` IN (? ,?) ==> Parameters: false(Boolean), test_env(String), 1(Long), 2(Long) <== Total: 3Copy the code

As observed above, the framework perfectly solves the 1+N query problem

Framework using

Maven references, please refer to the Fluent Mybatis documentation for details

    <dependency>
        <groupId>com.github.atool</groupId>
        <artifactId>form-service-meta</artifactId>
        <version>1.9.1</version>
    </dependency>
    <dependency>
        <groupId>com.github.atool</groupId>
        <artifactId>fluent-mybatis</artifactId>
        <version>1.9.1</version>
    </dependency>
    <dependency>
        <groupId>com.github.atool</groupId>
        <artifactId>fluent-mybatis-processor</artifactId>
        <version>1.9.1</version>
        <scope>provided</scope>
    </dependency>
Copy the code

See Fluent Mybatis for a complete example