Foxnic-SQL的模型关联

Foxnic-SQL (16) ——Foxnic-SQL的模型关联方法

概述

本节我们将用一个简单的例子,来说明对象之间的关联关系,以及 Foxnic-SQL 是如何处理这种关联关系的。首先,我们引入商城下单的简单业务模型,这个模型里面包括了商品、订单、订单明细以及收件人地址,这个模型足够简单,所以很容易分析出他们之间的关联关系。

本文中的示例代码均可在 https://gitee.com/LeeFJ/foxnic-samples 项目中找到。

业务分析

从下面这个ER图,我们可以看到四个表,订单(example_order)、订单明细(example_order_item)、商品(example_goods)、收件地址(example_address),如果按常规逻辑,我们从订单开始入手,一个订单包含若干商品,并投第到一个地址上。

不难分析出他们之间的关系:一个订单只会有一个地址,所以他们是一对一的;把这个关系反一下,同一个地址可能会收到多个订单,所以他们是一对多的关系;通常来讲,订单和商品是属于多对多关系,因为一个商品可以属于不同的订单,一个订单也可以包含多个商品;同理可以推得,地址和商品也是多对多的关系。但是这样的关系分析似乎有点搞脑子。

Foxnic-SQL 将这种进行简化,只有一对一或一对多两种关系。即,如果我们将上述的四个表转换为实体类,当某一个类要持有对方时,它要为对方设置单一属性还是 List 属性来判断两者关系。

例如 Order 类要持有 Address 类时,它只需要定义一个属性  private Address address; 就可以了,这是 Order 对于 Address  就是一对一的关系;再如,Order 类要持有 Goods 则需要为其定义一个列表 private List<Goods> goodsList; 才可以,这时Order 对于 Goods 就是一对多的关系。

Foxnic-SQL 要求尽可能分析清楚这些实体之间的关系,并配置到系统中,最终形成一个实体关系的大地图,当我们定义清楚A持有B所需要的属性类型,我们同时也就是构建了一个全局的对象模型,如图所示:

下面,我们用下单的这个例子,来系统说明 Foxnic-SQL 是如何完成关系搭建与模型的 Join。

生成表结构元数据

要使表之间的关系可以在 Java 代码中可配置,那就必须可以在 Java 代码中可以引用到表。所以,第一步的工作就是将数据表对象化,Foxnic-SQL 的代码生成模块已经实现了这部分工作,我只要做一些简单的配置即可完成调用生成 Java 形式的表结构,代码如下:

package com.leefj.foxnic.sql.demo.generator;

import com.github.foxnic.dao.spec.DAO;

import com.github.foxnic.generator.builder.constants.DBMetaClassFile;

import com.leefj.foxnic.sql.demo.config.DBInstance;

public class ExampleDBMetaGenerator {

    /**

* 运行main函数生成代码

* */

    public static void main(String[] args) throws Exception {

        ExampleDBMetaGenerator g = new ExampleDBMetaGenerator();

        g.buildDBMeta();

    }

    /**

* 生成DBMeta数据

* */

    private void buildDBMeta() {

        // 初始化 DAO 对象

        DAO dao= DBInstance.DEFAULT.dao();

        // 初始化全局设置

        GeneratorUtil.initGlobalSettings();

        // 初始化 DBMetaClassFile 构建器

        DBMetaClassFile dbMetaBuilder=new DBMetaClassFile(dao,GeneratorUtil.getProject(), "com.leefj.foxnic.sql.demo.config","ExampleTables");

        // 过滤与排除不需要的表

        dbMetaBuilder.setTableFilter(table->{

            table=table.toLowerCase();

            // 仅生成以 example_ 开头的表

            if(table.startsWith("example_")) return true;

            return false;

        });

        // 保存为 Java 代码

        dbMetaBuilder.save(true);

    }

}

运行 main 函数,将生成 com.leefj.foxnic.sql.demo.config.db.ExampleTables 类,这个类包含了相关业务表的表结构信息。由于代码较多此处不做展示,可签出完整项目 https://gitee.com/LeeFJ/foxnic-samples 查看。

生成基础实体

所谓生成基础实体就是完全按照表结构生成实体类,每个表按默认的命名转换器生成实体,对应的表字段则生成相应的类的成员属性。

package com.leefj.foxnic.sql.demo.generator;

import com.github.foxnic.commons.project.maven.MavenProject;

import com.github.foxnic.dao.spec.DAO;

import com.github.foxnic.generator.builder.model.PoClassFile;

import com.github.foxnic.generator.config.ModuleContext;

import com.github.foxnic.sql.meta.DBTable;

import com.leefj.foxnic.sql.demo.config.DBInstance;

import com.leefj.foxnic.sql.demo.config.db.ExampleTables;

/**

* 实体类生成器

* */

public class EntityGenerator {

    public static interface  Config  {

        void config(PoClassFile poType);

    }

    private static final String BASE_PACKAGE = "com.leefj.foxnic.sql.demo.app";

    /**

* 需要首先运行 ExampleDBMetaGenerator 生成 ExampleTables 类

* */

    public static void main(String[] args) {

        EntityGenerator generator = new EntityGenerator();

        // 生成商品实体类

        generator.generate(ExampleTables.EXAMPLE_GOODS.$TABLE);

        // 生成订单实体类

        generator.generate(ExampleTables.EXAMPLE_ORDER.$TABLE);

        // 生成订单明细实体类

        generator.generate(ExampleTables.EXAMPLE_ORDER_ITEM.$TABLE);

        // 生成地址实体类

        generator.generate(ExampleTables.EXAMPLE_ADDRESS.$TABLE);

    }

    /**

    * 按表生成

    * */

    public void generate(DBTable table) {

        generate(table,null);

    }

    /**

    * 按表生成

    * */

    public void generate(DBTable table,Config config) {

        DAO dao = DBInstance.DEFAULT.dao();

        MavenProject project = GeneratorUtil.getProject();

        String pkg = table.name().split("_")[0];

        String prefix = pkg + "_";

        ModuleContext context = new ModuleContext(GeneratorUtil.initGlobalSettings(),table,prefix,BASE_PACKAGE + "." + pkg);

        context.setDomainProject(project);

        context.setServiceProject(project);

        context.setDAO(dao);

        if(config!=null) {

            config.config(context.getPoClassFile());

        }

        context.buildPo();

        context.buildVo();

        context.buildService();

    }

}

运行 main 方法将生成实体类代码,以上代码生成 PO、VO 对象以及这些类型对应的元数据类型,代码生成后如图所示:

加入扩展属性

下面我们要为这些实体类加入扩展属性,正如我们刚刚在上文中分析的那样,Order 类要持有 Address 类时,需要定义一个属性  private Address address; Order 类要持有 Goods 则需要为其定义一个列表 private List<Goods> goodsList; 才可以。我们修改上一步中的 EntityGenerator 类,调整 main 方法如下:

/**

* 需要首先运行 ExampleDBMetaGenerator 生成 ExampleTables 类

* */

public static void main(String[] args) {

    EntityGenerator generator = new EntityGenerator();

    // 生成商品实体类

    generator.generate(ExampleTables.EXAMPLE_GOODS.$TABLE, poType -> {

        // Goods 对象 通过 orderList 属性持有 Order

        poType.addListProperty(Goods.class,"orderList","订单明细商品","订单明细商品");

        // Goods 对象 通过 addressList 属性持有 Address

        poType.addListProperty(Address.class,"addressList","收件地址","收件地址,包括收件人以及手机号码");

        // Goods 对象 通过 itemList 属性持有 OrderItem

        poType.addListProperty(OrderItem.class,"itemList","订单明细","订单明细");

    });

    // 生成订单实体类

    generator.generate(ExampleTables.EXAMPLE_ORDER.$TABLE , poType -> {

        // Order 对象 通过 goodsList 属性持有 Goods

        poType.addListProperty(Goods.class,"goodsList","订单明细商品","订单明细商品");

        // Order 对象 通过 address 属性持有 Address

        poType.addSimpleProperty(Address.class,"address","收件地址","收件地址,包括收件人以及手机号码");

        // Order 对象 通过 itemList 属性持有 OrderItem

        poType.addListProperty(OrderItem.class,"itemList","订单明细","订单明细");

    });

    // 生成订单明细实体类

    generator.generate(ExampleTables.EXAMPLE_ORDER_ITEM.$TABLE, poType -> {

        // OrderItem 对象 通过 goodsList 属性持有 Goods

        poType.addSimpleProperty(Goods.class,"goods","订单明细商品","订单明细商品");

        // OrderItem 对象 通过 address 属性持有 Address

        poType.addSimpleProperty(Address.class,"address","收件地址","收件地址,包括收件人以及手机号码");

        // OrderItem 对象 通过 order 属性持有 Order

        poType.addListProperty(Order.class,"order","订单","订单");

    });

    // 生成地址实体类

    generator.generate(ExampleTables.EXAMPLE_ADDRESS.$TABLE, poType -> {

        // Address 对象 通过 goodsList属性 持有 Goods

        poType.addListProperty(Goods.class,"goodsList","订单明细商品","订单明细商品");

        // Address 对象 通过 orderList 持有 Order

        poType.addListProperty(Address.class,"orderList","收件地址","收件地址,包括收件人以及手机号码");

        // Address 对象 通过 itemList 持有 OrderItem

        poType.addListProperty(OrderItem.class,"itemList","订单明细","订单明细");

    });

}

通过上面的代码,我们定义了任意对象如果要持有对方时需要定义的属性。当然,这些定义的属性中有部分没有实际的业务意义,我没这里仅仅是从技术角度来说明对象之间关联属性的定义方法。再次运行

注意,代码生成并非只是本文中展示的一种范式,小伙伴们可按需要自行实现。

配置关联关系

前面的步骤,我们生成了实体类以及实体类的扩展属性,有了扩展属性后我们要为这些扩展属性配置好关联关系。关联关系在 ExampleRelationManager 类中配置,如下代码所示:

import com.github.foxnic.dao.relation.RelationManager;

import com.leefj.foxnic.sql.demo.app.domain.example.meta.AddressMeta;

import com.leefj.foxnic.sql.demo.app.domain.example.meta.GoodsMeta;

import com.leefj.foxnic.sql.demo.app.domain.example.meta.OrderItemMeta;

import com.leefj.foxnic.sql.demo.app.domain.example.meta.OrderMeta;

import com.leefj.foxnic.sql.demo.config.db.ExampleTables;

public class ExampleRelationManager extends RelationManager {

    @Override

    protected void config() {

        this.setupOrder();

    }

    public void setupOrder() {

        // 以下是 Order 扩展属性的关联关系配置

        // Order.address 属性通过 EXAMPLE_ORDER.ADDRESS_ID 字段关联到 EXAMPLE_ADDRESS.ID 字段

        this.property(OrderMeta.ADDRESS_PROP)

            .using(ExampleTables.EXAMPLE_ORDER.ADDRESS_ID).join(ExampleTables.EXAMPLE_ADDRESS.ID)

            // 由于订单地址不常变动,可以开启缓存

            .cache(true);

        // Order.goodsList 属性通过 EXAMPLE_ORDER.ID 字段关联到 EXAMPLE_ORDER_ITEM.ORDER_ID 字段

        // 再通过 EXAMPLE_ORDER_ITEM.GOODS_ID 字段关联到 EXAMPLE_GOODS.ID 字段

        this.property(OrderMeta.GOODS_LIST_PROP)

            .using(ExampleTables.EXAMPLE_ORDER.ID).join(ExampleTables.EXAMPLE_ORDER_ITEM.ORDER_ID)

            .using(ExampleTables.EXAMPLE_ORDER_ITEM.GOODS_ID).join(ExampleTables.EXAMPLE_GOODS.ID)

            // 订单一旦生成,订单明细不再变动,开启缓存

            .cache(true);

        // Order.itemList 属性通过 EXAMPLE_ORDER.ID 字段关联到 EXAMPLE_ORDER_ITEM.ORDER_ID 字段

        this.property(OrderMeta.ITEM_LIST_PROP)

            .using(ExampleTables.EXAMPLE_ORDER.ID).join(ExampleTables.EXAMPLE_ORDER_ITEM.ORDER_ID)

            .cache(true);

        // 以下是 OrderItem 扩展属性的关联关系配置

        // OrderItem.order 属性通过 EXAMPLE_ORDER_ITEM.ORDER_ID 字段关联到 EXAMPLE_ORDER.ID 字段

        this.property(OrderItemMeta.ORDER_PROP)

            .using(ExampleTables.EXAMPLE_ORDER_ITEM.ORDER_ID).join(ExampleTables.EXAMPLE_ORDER.ID)

            .cache(true);

        // OrderItem.goods 属性通过 EXAMPLE_ORDER_ITEM.GOODS_ID 字段关联到 EXAMPLE_GOODS.ID 字段

        this.property(OrderItemMeta.GOODS_PROP)

            .using(ExampleTables.EXAMPLE_ORDER_ITEM.GOODS_ID).join(ExampleTables.EXAMPLE_GOODS.ID)

            .cache(true);

        // 以下是 Goods 扩展属性的关联关系配置

        this.property(GoodsMeta.ORDER_LIST_PROP)

            .using(ExampleTables.EXAMPLE_GOODS.ID).join(ExampleTables.EXAMPLE_ORDER_ITEM.GOODS_ID)

            .using(ExampleTables.EXAMPLE_ORDER_ITEM.ORDER_ID).join(ExampleTables.EXAMPLE_ORDER.ID)

            .cache(true);

        this.property(GoodsMeta.ADDRESS_LIST_PROP)

            .using(ExampleTables.EXAMPLE_GOODS.ID).join(ExampleTables.EXAMPLE_ORDER_ITEM.GOODS_ID)

            .using(ExampleTables.EXAMPLE_ORDER_ITEM.ORDER_ID).join(ExampleTables.EXAMPLE_ORDER.ID)

            .using(ExampleTables.EXAMPLE_ORDER.ADDRESS_ID).join(ExampleTables.EXAMPLE_ADDRESS.ID)

            .cache(true);

        this.property(GoodsMeta.ITEM_LIST_PROP)

            .using(ExampleTables.EXAMPLE_GOODS.ID).join(ExampleTables.EXAMPLE_ORDER_ITEM.GOODS_ID)

            .cache(true);

        // 以下是 Address 扩展属性的关联关系配置

        this.property(AddressMeta.ORDER_LIST_PROP)

            .using(ExampleTables.EXAMPLE_ADDRESS.ID).join(ExampleTables.EXAMPLE_ORDER.ADDRESS_ID)

            .cache(true);

        this.property(AddressMeta.ITEM_LIST_PROP)

            .using(ExampleTables.EXAMPLE_ADDRESS.ID).join(ExampleTables.EXAMPLE_ORDER.ADDRESS_ID)

            .using(ExampleTables.EXAMPLE_ORDER.ID).join(ExampleTables.EXAMPLE_ORDER_ITEM.ORDER_ID)

            .cache(true);

        this.property(AddressMeta.GOODS_LIST_PROP)

            .using(ExampleTables.EXAMPLE_ADDRESS.ID).join(ExampleTables.EXAMPLE_ORDER.ADDRESS_ID)

            .using(ExampleTables.EXAMPLE_ORDER.ID).join(ExampleTables.EXAMPLE_ORDER_ITEM.ORDER_ID)

            .using(ExampleTables.EXAMPLE_ORDER_ITEM.GOODS_ID).join(ExampleTables.EXAMPLE_GOODS.ID)

            .cache(true);

    }

}

同样,我们在关系配置中几乎配置了所有的两两关系,当然某些关系并没有业务意义,仅做技术展示。关系配置时还可以有其它更多的控制,例如额外的条件、排序等,请小伙伴们自行探索。

通过Service拉取模型

通过之前的代码生成,不仅生成的PO、VO类,同时也生成了 Service 类,通过 DBInstance 可以拿到这个 Service 对象,若是在 Web 环境下,直接使用 Spring 的依赖注入即可拿到 Service 对象。通过 Service 拉取模型的示例代码如下:

/**

* 通过 Service 的 Join 方法完成属性数据的填充操作

* */

@Test

public void demo_1() {

    // 获得 Service

    IOrderService orderService=DBInstance.DEFAULT.getService(OrderServiceImpl.class);

    // 查询所有订单

    List<Order> orders= orderService.queryList(new Order());

    // 填充订单属性

    orderService.join(orders, OrderMeta.ADDRESS,OrderMeta.ITEM_LIST,OrderMeta.GOODS_LIST);

    // 输出填属性填充后的订单列表

    System.out.println(JSONUtil.format(JSONUtil.toJSONArray(orders)));

}

以上代码首先查询了订单列表,此时获得的订单列表并未填充扩展属性值,在调用 Service 的 join 方法,指定需要 Join 的属性名称即可。

Service 的 join 方法事实上是间接调用了 DAO 的 join 方法,本例中 join 了三个属性,这三个属性的查询与 join 过程并非顺序执行的,而是利用了 java 的 fork...join 框架进行了并行操作。join 过程中会有一个输出,如下所示:

JOIN(DATA) FORK(21):128 , el=7 >>>

Order :: List<Goods> goodsList , properties : id , route example_order to example_goods

example_order_item( goods_id ) = example_goods( id )

第一行的 JOIN(DATA) 是指当前的 join 操作是数据操作,另外还有一种 JOIN(SEARCH),在数据搜索时用到,吗这里不做展开。FORK(21):128,其中 21 是指线程ID,128 是指 fork 时拆分的行数。这里是指如果 orders 的元素个数超过128,那么就按128的规格进行 frok 任务拆解。 el=7 是指当前 orders 实际数量是7个。

Order :: List<Goods> goodsList 是指当前正在处理 Order 类的 goodsList 属性, properties : id 是指关联的起始属性是 Order.id , route example_order to example_goods 是指从 example_order 表路由到 example_goods 表。example_order_item( goods_id ) = example_goods( id ) 展示了本次 join 的关联关系。这些关联关系我们在 ExampleRelationManager 类里做了配置。

最后,我们可以在 SQL 日志上看到 join 最终执行的SQL语句:

┏━━━━━ SQL [ SELECT t_0.* , t_1.order_id join_f0 , t_0.id join_fs_example_goods_sf_nioj_id ... ] ━━━━━

┣ 语句:SELECT t_0.* , t_1.order_id join_f0 , t_0.id join_fs_example_goods_sf_nioj_id , t_0.id pk_join_fs_example_goods_sf_nioj_id , t_1.goods_id join_fs_example_order_item_sf_nioj_goods_id , t_1.order_id join_fs_example_order_item_sf_nioj_order_id , t_1.id pk_join_fs_example_order_item_sf_nioj_id FROM (select * from example_goods WHERE ( deleted= ? ) ) t_0

join (select * from example_order_item WHERE ( deleted= ? ) ) t_1 on t_1.goods_id = t_0.id

WHERE t_1.order_id IN ( ? , ? , ? , ? , ? , ? , ? ) AND t_1.deleted= ?

┣ 参数:{"PARAM_1":0,"PARAM_9":"651345265944428544","PARAM_8":"651017423926853632","PARAM_10":0,"PARAM_7":"583188757629370368","PARAM_6":"651024562955223040","PARAM_5":"651017546694131712","PARAM_4":"583028022102200320","PARAM_3":"651024388312793088","PARAM_2":0}

┣ 执行:SELECT t_0.* , t_1.order_id join_f0 , t_0.id join_fs_example_goods_sf_nioj_id , t_0.id pk_join_fs_example_goods_sf_nioj_id , t_1.goods_id join_fs_example_order_item_sf_nioj_goods_id , t_1.order_id join_fs_example_order_item_sf_nioj_order_id , t_1.id pk_join_fs_example_order_item_sf_nioj_id FROM (select * from example_goods WHERE ( deleted= 0 ) ) t_0

join (select * from example_order_item WHERE ( deleted= 0 ) ) t_1 on t_1.goods_id = t_0.id

WHERE t_1.order_id IN ( '651024388312793088' , '583028022102200320' , '651017546694131712' , '651024562955223040' , '583188757629370368' , '651017423926853632' , '651345265944428544' ) AND t_1.deleted= 0

┣ 结果:

┣━ 耗时:84ms , start = 1672796438302

┣━ 返回:RcdSet,size=2

┣ TID:null

┗━━━━━ SQL [ SELECT t_0.* , t_1.order_id join_f0 , t_0.id join_fs_example_goods_sf_nioj_id ... ] ━━━━━

通过DAO拉取模型

DAO 本身也有 join 方法,用法和 Service 的 join 方法相似,这里不做展开。本例中,我们使用 DAO 的 fill...with 方法拉取模型。

/**

* 通过 DAO 对象的 fill...with 方法完成属性数据的填充

* */

@Test

public void demo_2() {

    // 获得 Service

    IOrderService orderService=DBInstance.DEFAULT.getService(OrderServiceImpl.class);

    // 查询所有订单

    List<Order> orders= orderService.queryList(new Order());

    DAO dao=orderService.dao();

    // 通过 dao 的 fill 方法填充 orders 列表里面元素的扩展属性

    dao.fill(orders)

        // 填充 orders 里面每个元素的 address 属性

        .with(OrderMeta.ADDRESS)

        // 填充 orders 里面每个元素的 itemList 属性

        .with(OrderMeta.ITEM_LIST)

        // 填充 orders 里面每个元素的 goodsList 属性

        .with(OrderMeta.GOODS_LIST)

        // 执行最终的 join 操作

        .execute();

    // 输出填属性填充后的订单列表

    System.out.println(JSONUtil.format(JSONUtil.toJSONArray(orders)));

}

通过还可以进一步拉取级联模型:

/**

* 通过 DAO 对象的 fill...with 方法完成属性数据的级联填充

* */

@Test

public void demo_3() {

    // 获得 Service

    IOrderService orderService=DBInstance.DEFAULT.getService(OrderServiceImpl.class);

    // 查询所有订单

    List<Order> orders= orderService.queryList(new Order());

    DAO dao=orderService.dao();

    // 通过 dao 的 fill 方法填充 orders 列表里面元素的扩展属性

    dao.fill(orders)

        // 填充 orders 里面每个元素的 address 属性,并填充  address 属性的 orderList 属性

        .with(OrderMeta.ADDRESS,AddressMeta.ORDER_LIST)

        // 填充 orders 里面每个元素的 address 属性,并填充  address 属性的 goodsList 属性

        .with(OrderMeta.ADDRESS,AddressMeta.GOODS_LIST)

        // 填充 orders 里面每个元素的 itemList 属性

        .with(OrderMeta.ITEM_LIST)

              // 填充 orders 里面每个元素的 goodsList 属性

      .with(OrderMeta.GOODS_LIST)

        // 执行最终的 join 操作

        .execute();

    // 输出填属性填充后的订单列表

    System.out.println(JSONUtil.format(JSONUtil.toJSONArray(orders)));

}

刻画模型

通过上面的两个示例,我们注意到,我们在拉取对象时,并未指定字段,这样就会拉出多余的字段,为了精确刻画需要拉取的模型字段,我们可以为 fill...with 指定 fields , 如下代码所示:

/**

* 通过 DAO 对象的 fill...with 方法完成属性数据的填充,并用 fields 指定查询字段

* */

@Test

public void demo_4() {

    // 获得 Service

    IOrderService orderService=DBInstance.DEFAULT.getService(OrderServiceImpl.class);

    // 查询所有订单

    List<Order> orders= orderService.queryList(new Order());

    DAO dao=orderService.dao();

    // 构建 Order 对象需要查询的字段

    FieldsBuilder orderFields=FieldsBuilder.build(dao, ExampleTables.EXAMPLE_ORDER.$TABLE);

    orderFields.addAll().removeDBTreatyFields();

    // 构建 address 对象需要的字段

    FieldsBuilder addressFields=FieldsBuilder.build(dao, ExampleTables.EXAMPLE_ADDRESS.$TABLE);

    addressFields.addAll().removeDBTreatyFields();

    // 通过 dao 的 fill 方法填充 orders 列表里面元素的扩展属性

    dao.fill(orders)

        // 填充 orders 里面每个元素的 address 属性

        .with(OrderMeta.ADDRESS)

        // 填充 orders 里面每个元素的 itemList 属性

        .with(OrderMeta.ITEM_LIST)

        // 填充 orders 里面每个元素的 goodsList 属性

        .with(OrderMeta.GOODS_LIST)

        // 指定字段

        .fields(orderFields,addressFields)

        // 执行最终的 join 操作

        .execute();

    // 输出填属性填充后的订单列表

    System.out.println(JSONUtil.format(JSONUtil.toJSONArray(orders)));

}

模型装配的原则

从整个示例的过程,大家注意到,示例中的绝大多是代码是生成的,在代码生成的基础上,我们配置两个对象之间的引用关系和关联关系。

Foxnic-SQL 的模型构建过程是按需拉取的,不同接口需要不同的数据,开发人员通过 Service 的 join 方法或 DAO 的 fill...with 方法手动指定本次要拉取的属性范围,使拉取过程更加符合接口要求。

事实上,引用关系和关联关系在一个系统中一般都是固定的,唯一不同的是在不同的场合拉取模型的范围,这个由开发人员控制,更加灵活方便。

小结

本节主要介绍了在 Foxni-SQL 中使用 join 方法填充对象模型。基本步骤包括,生成数据库表元数据、生成 PO、VO、Service 代码、配置关联关系、使用 Service 或 DAO 完成模型装配。

相关项目

https://gitee.com/LeeFJ/foxnic

https://gitee.com/LeeFJ/foxnic-web

https://gitee.com/lank/eam

https://gitee.com/LeeFJ/foxnic-samples

官方文档

http://foxnicweb.com/docs/doc.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容