Visual Angle: Comparison of jfinal's Model and Beetlsql

JFinal is currently the most concerned project in git.oschina.net java.

I have personally used JFinal to develop projects with hundreds of tables. Always make a summary after the project is over

This article is to introduce some experiences developed in the project.

  1. The Model development of JFinal will be listed first (just the use of Model)
  2. Use Beetlsql to replace JFinal's Model (give a cooler coding method)
  3. Comparing the two when writing multi-conditional query sql, (that is, conditional judgment is required, and null does not participate in the query)

1. Model development of JFinal

JFinal's ORM is called JFinal's Model below

This is the addition, modification and deletion of the Model

jfinal entity class 1

public class Elephant extends Model<Elephant> {
    public static final Elephant Dao = new Elephant();
    public List<Integer> findIds() {
        return Db.query("select id from tb_bird");
    }
}

 

jfinal test case 1

@Log4j
public class ElephantTest {
    @Test
    public void testChainSave() {
        // 无需要创建字段
        new Elephant().set("age", 17).set("name", "jfinal").set("desc", "mvc + orm").save();
    }

    @Test
    public void testUpdate() {
        Elephant elephant = Elephant.Dao.findById(1);
        elephant.set("age", 18).update();
    }

    @Test
    public void testDelete() {
        Elephant elephant = Elephant.Dao.findById(1);
        elephant.delete();
    }
}

 

    To be honest, when I first looked at the example, it felt good. After all, you don't need to define any fields yourself to save data directly to the table.

    This is also used for development at the beginning, but if there are multiple places where this code is used, and some fields need to be changed during the product development process, then a small nightmare begins. After all, there is no tool for this kind of detection. It is also legal to write the table field name directly as a string. Then the only thing that can be done is to search for the place where this class is used in the whole project, and then check one by one and need to look very carefully. Because this is a manual inspection, the tool It can't help you very much.

Another point is to develop in this way that you have to remember each field of each table, and can't make mistakes (you need to manually type the name of the database table field in the code ).

If it's a few tables and few fields, there's nothing wrong with playing with the code. If it .

So it is agreed that the entity class code must provide getters and setters. In this way, tools can provide error detection

So the entity class in the project becomes the following way

jfinal entity class 2 getter, setter

public class Elephant extends Model<Elephant> {
    public static final Elephant Dao = new Elephant();

    public int getId() {
        return this.getInt("id");
    }

    public Elephant setId(int id) {
        this.set("id", id);
        return this;
    }

    public int getAge() {
        return this.getInt("age");
    }

    public Elephant setAge(int age) {
        this.set("age", age);
        return this;
    }

    public String getName() {
        return this.getStr("name");
    }

    public Elephant setName(String name) {
        this.set("name", name);
        return this;
    }

    public String getDesc() {
        return this.getStr("desc");
    }

    public Elephant setDesc(String desc) {
        this.set("desc", desc);
        return this;
    }

    public List<Integer> findIds() {
        return Db.query("select id from tb_bird");
    }
}

The entity class code provides getters and setters ( in order to maintain the chain style, the setters return themselves ). This tool can provide error detection, which seems to be very good.

jfinal test case 2

This is the test case after adding getter and setter

@Log4j
public class ElephantChainTest {
    @Test
    public void testChainSave() {
        new Elephant().setAge(17).setName("jfinal").setDesc("mvc + orm").save();
    }

    @Test
    public void testUpdate() {
        Elephant elephant = Elephant.Dao.findById(1);
        elephant.setAge(18).update();
    }

    @Test
    public void testDelete() {
        Elephant elephant = Elephant.Dao.findById(1);
        elephant.delete();
    }
}

 

    Well, the Model entity class all have getter and setter methods. Even if the table business needs to change the name of a field, it doesn't matter, I just change the method name corresponding to this entity class. The tool will give us the error in the code. This property (field) is used in one line. And there is no need to manually type the field name where the entity class is used. Very good. Much more convenient!

    But what I want to tell you is that these getter and setter codes are all manually typed. At first, I wanted to use JFinal's Model to save time. There is no doubt that this did not save my time, and it wasted a lot. time.

Here is just the entity class corresponding to one of the tables (there are very few fields, because only 4 fields are used here as an example). If there are hundreds of entity classes and some entity classes have 8, 15 or even more attributes (yes , you read that right, you will be more and more happy ).

    The above mentioned why these codes are manually typed . Can't the tool generate getters and setters? I want to say that it can be generated, but there are prerequisites for the tools to generate these codes. These conditions are what you must show. Member properties of the class.

So unfortunately, you can't enjoy the convenience brought by the tool

    Well, the code stipulates that the Model entity class must provide getter and setter methods. Then the code must be commented. So the code becomes as follows:

public class Elephant extends Model<Elephant> {
    public static final Elephant Dao = new Elephant();

    /**
     * @return 年龄
     */
    public int getAge() {
        return this.getInt("age");
    }

    /**
     * @param age 年龄
     * @return this
     */
    public Elephant setAge(int age) {
        this.set("age", age);
        return this;
    }

    /**
     * @return 名字
     */
    public String getName() {
        return this.getStr("name");
    }

    /**
     * @param name 名字
     * @return this
     */
    public Elephant setName(String name) {
        this.set("name", name);
        return this;
    }

    /**
     * @return 描述
     */
    public String getDesc() {
        return this.getStr("desc");
    }

    /**
     * @param desc 描述
     * @return this
     */
    public Elephant setDesc(String desc) {
        this.set("desc", desc);
        return this;
    }

    public List<Integer> findIds() {
        return Db.query("select id from tb_bird");
    }
}

 

    This changed from an entity class that was originally only 1 line of code to this. If you are reading this text, it seems that it has become like this and nothing. But you want to know how much it takes to have getters, setters and comments How much time? (Please create an entity with 8 fields by yourself to play).

I'm wondering why I spend so much time creating a model ( I comfort myself like this, it also belongs to aerobic exercise ).

JAVA extremely fast WEB+ORM framework JFinal

JFinal is an extremely fast WEB + ORM framework based on the Java language. Its core design goals are rapid development, less code, easy learning, powerful functions, light weight, easy expansion, and Restful. With all the advantages of the Java language, it also has the development efficiency of dynamic languages ​​such as ruby ​​and python! Save more time for you to spend with lovers, family and friends ;)

    The development of block references is rapid and the amount of code is small. It is not clear whether the amount of code in JFianl is small or the amount of code in JFinal development projects is small. At least I think that if the Model in the project shows that the amount of getter and setter code is provided, the amount of code is already huge and The coding efficiency is extremely slow (it's just the feeling that JFinal's ORM gives me. It's not exactly the feeling, after all, the code example above contains what I want to say).

Neither is showing offers, nor is not showing offers. Dilemma.

2. Use Beetlsql to replace JFinal's Model

The combination of beetlsql + java8 + lombok achieves the most concise code effect and is more readable (hereinafter referred to as the second encoding method)

(Minimal Version) Entity Class 1

@Data
@FieldDefaults(level = AccessLevel.PRIVATE)  // 属性默认都是private
@lombok.experimental.Accessors(chain = true) // 链式编程
@Table(name = "tb_bird")                     // 实体类与表映射
public class Bee implements $Sql { // 实现$Sql接口, 可以在对象上直接使用 save, update, delete 方法 (不是必须的)
    int id;
    /** 年龄 */
    Integer age;
    /** 名字 */
    String name;
    /** 描述 */
    String desc;
}

lombok.@Data automatically provides getter, setter, toString and other methods. So there is no need to generate getter and setter methods in the code (there will be annotations on the methods), which makes the entity class cleaner.

In the above example, the entity class only needs to implement the interface without any inheritance to achieve the desired effect. ( Effective Java Item 18: Interfaces are better than abstract classes )

test class

@Log4j
public class BeeChainTest {
    @Test
    public void testChainSave() {
        new Bee().setAge(17).setName("beetl").setDesc("beetlsql + orm").save();
    }

    @Test
    public void testUpdate() {
        Bee bee = Bee.Dao.$.unique(1);
        bee.setAge(18).update();
    }

    @Test
    public void testDelete() {
        Bee bee = Bee.Dao.$.unique(1);
        bee.delete();
    }
}

    It can be seen that in terms of ORM approach. I prefer the latter combination.

  1. Automatically have getter setter, no need for manual tapping ( aerobic exercise )
  2. The code is clearer and more concise, and it can even be said that the amount of code is reduced. It allows you to develop faster.

    In the Model of JFinal above and the following method, if we are appending a field (and annotated) to both entity classes, the second method is undoubtedly the most convenient.

    Because JFinal's Model needs 14 lines of code to add a field's getter setter and add comments, while the second method only requires two lines (you can count yourself in the example given above). That is to say, each time the entity class adds a field , your Model class has 12 more lines of code than using the second method (7 times the amount of code, this is still in the case where you manually type the name of the getter setter is 100% correct, getName getNema does not have this spelling accident ).

    So in order to prevent this kind of accident, you may say that I can first define the attribute and determine the type in the Model, and then use the tool to generate the getter setter, make some changes, and then delete the defined attribute (so that there will be no spelling accident). , and enjoy the convenience of tools to generate code). I want to say that you are very humorous . And I want to say to you that I used to be so humorous .

    To be honest, let's say the class has 10 fields. So one is 140 lines of code and the other is 20 lines of code. Which one do you prefer? I don't know what you think, when I look at 140 lines of code, I have to Scrolling up and down in the tool. And the second way I can see at a glance what's going on.

If the name of the field is changed, then the Model needs to be changed in 7 places (in other words, 7 places to be maintained ) . The second method only needs to be changed in 1 place . (This only calculates the changes in the entity class, and does not consider the changes in the project for the time being. where the entity class is referenced elsewhere)

 

Chain programming of beetlsql

The bad news is that beetlsql doesn't support chain programming at the time of this post

The author ( @小大福) replied on the forum as follows:

Because the writing method of the setter method is not the standard writing method of Javabean, beetl cannot recognize this as an attribute, so there will be the generated sql statement just now.

beetlsql will support reading fields instead of getters and setters in the next version, but it will take some time

Of course, if there is bad news, there will be good news . Careful friends will find that the author will provide

 

 

3. Compare the two when writing multi-condition query sql

    Of course, if you are still not attracted by the comparison of the above models, then this section will show the sharp side of Beetlsql (it will not be listed in detail here, just a few simple methods). More For details, get the information on the beetlsql official website .

There are multiple conditional parameters (that is, conditional judgment is required, and those that are null do not participate in the query)

Here are three methods to perform the same SQL query for comparison

  • Query all ids - findIds
  • Three conditional parameters to query a single object - findOne
  • Two conditional arguments to query a list of objects - finds

The current code of the jfinal Model class plus the code after these three queries

public class Elephant extends Model<Elephant> {
    public static final Elephant Dao = new Elephant();

    /**
     * @return 年龄
     */
    public int getAge() {
        return this.getInt("age");
    }

    /**
     * @param age 年龄
     * @return this
     */
    public Elephant setAge(int age) {
        this.set("age", age);
        return this;
    }

    /**
     * @return 名字
     */
    public String getName() {
        return this.getStr("name");
    }

    /**
     * @param name 名字
     * @return this
     */
    public Elephant setName(String name) {
        this.set("name", name);
        return this;
    }

    /**
     * @return 描述
     */
    public String getDescription() {
        return this.getStr("description");
    }

    /**
     * @param description 描述
     * @return this
     */
    public Elephant setDescription(String description) {
        this.set("description", description);
        return this;
    }

    public List<Integer> findIds() {
        return Db.query("select id from tb_bird");
    }

    public Elephant findOne(int age, String name, String description) {
        StringBuilder sql = new StringBuilder("select * from tb_bird where 1 = 1");
        List<Object> pars = new LinkedList<>();

        if (age != 0) {
            sql.append(" and age = ? ");
            pars.add(age);
        }

        if (name != null) {
            sql.append(" and name = ? ");
            pars.add(name);
        }

        if (description != null) {
            sql.append(" and description = ? ");
            pars.add(description);
        }

        return this.findFirst(sql.toString(), pars.toArray());
    }

    public List<Elephant> finds(int age, String name) {
        StringBuilder sql = new StringBuilder("select * from tb_bird where 1 = 1");
        List<Object> pars = new LinkedList<>();

        if (age != 0) {
            sql.append(" and age = ? ");
            pars.add(age);
        }

        if (name != null) {
            sql.append(" and name = ? ");
            pars.add(name);
        }

        return Db.query(sql.toString(), pars.toArray());
    }
}

 

jfinal Model query test case

@Log4j
public class ElephantFindTest {
    @Before
    public void bf() {
        Config.jfinalInit();
    }

    @Test
    public void findIds() {
        // 查询所有主键
        List<Integer> ids = Elephant.Dao.findIds();
        log.info(ids);
    }

    @Test
    public void findOne() {
        Elephant one = Elephant.Dao.findOne(18, "a", "n");
        log.info(one);
    }

    @Test
    public void finds() {
        List<Elephant> list = Elephant.Dao.finds(18, "b");
        log.info(list);
    }
}

 

 

(Minimalist version) The current code of the second method class plus the code after these three queries

@Data
@FieldDefaults(level = AccessLevel.PRIVATE)  // 属性默认都是private
@lombok.experimental.Accessors(chain = true) // 链式编程
@Table(name = "tb_bird")                     // 实体类与表映射
public class Bee implements $Sql { // 实现$Sql接口, 可以在对象上直接使用 save, update, delete 方法 (不是必须的)
    int id;
    /** 年龄 */
    Integer age;
    /** 名字 */
    String name;
    /** 描述 */
    String description;

    // 自定义sqlDao处理类. (如果没有自定义sql就没必要定义这个接口)
    public interface Dao extends $Mapper<Bee> {
        /** 约定使用$符号表示自己 (这句代码也不是必须的) */
        Dao $ = mapper(Dao.class);

        /**
         * 使用注解忽略默认的内置处理
         * 也可以写成下面的方式, 这里只是示例使用
         * @return 查询表所有主键
         */
        @Ignore
        default List<Integer> findIds() {
            return $().execute(new SQLReady("select id from tb_bird"), Integer.class);
        }

        // 这里无需再写接口实现类, 只需要在bee.md文件写sql
        @SqlStatement(params = "age,name,description")
        Bee findOne(int age, String name, String description);

        // 这里无需再写接口实现类, 只需要在bee.md文件写sql
        @SqlStatement(params = "age,name")
        List<Bee> finds(int age, String name);
    }
}

 

The second way to query test cases

@Log4j
public class BeeFindTest {
    @Before
    public void bf() {
        Config.dbInit();
    }

    @Test
    public void findIds() {
        // 查询所有主键
        List<Integer> ids = Bee.Dao.$.findIds();
        log.info(ids);
    }

    @Test
    public void findOne() {
        Bee one = Bee.Dao.$.findOne(18, "a", "n");
        log.info(one);
    }

    @Test
    public void finds() {
        List<Bee> list = Bee.Dao.$.finds(18, "b");
        log.info(list);
    }
}

    The advantages and disadvantages can be seen from the above two sample codes:

Try to think about it, suppose this class has 15 fields and there are about 10 methods for interacting with the database. Here is just a list of three methods of a class (and the maximum query conditions are only three, Model requires too much code Then I believe that there are conditional parameters in your project and there are no more than three), the number of tables in the actual project is much more terrifying than this. At this time, it is not a hypothesis, but a real one .

About Model

    Model writes all the places that deal with the database in a class. , you can imagine how large and bloated the code of this Model is, and the maintenance is time-consuming

About beetlsql

    There is no need to write entity classes that deal with the database. Just define the interface and write the corresponding md file

All sql statements are stored in the md file corresponding to the class for management (if simple statements can be written directly in the interface, just declare default)

In the case of two implementations of the same function, the second way to write the code is extremely concise, and even the preview code is many times faster.

 

Here is a screenshot of the second method and md (sql) file management

 

finally

The above are some summaries and discussions developed by myself in projects with many tables.

Of course, this article is not to say that JFinal's Model is not easy to use or the shortcomings of jfinal, but to find a (this is only self-consideration) better coding method that can replace Model.

Here is a beetlsql demo for your reference.

The last thing I want to say is that once you use beetlsql and code in the way recommended above. You can't go back, because it's really cool.

Have used (Hibernate Mybatis) before using beetlsql, you don't want to go back

 

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324537930&siteId=291194637