2023年4月

main 方法

public class Solution {
    public static void main(String[] args) {
        Person person = new Person();
        person.hello();
    }
}

class Person {
    public void hello() {
        System.out.println("hello");
    }
}

源文件名是 Solution.java,这是因为文件名必须与 public 类的名字相匹配。在一个源文件中,只能有一个公有类,但可以有任意数目的非公有类。

在这个示例程序中包含两个类:Person 类和带有 public 访问修饰符的 Solution 类。Solution 类包含了 main 方法。

当编译这段源代码的时候,编译器将在目录下创建两个 class 文件:Solution.class 和 Person.class。

将程序中包含 main 方法的类名提供给字节码解释器,以便启动这个程序:java Solution。字节码解释器开始运行 Solution 类的 main 方法中的代码。

image-20230326211666666782.png

理解方法调用

下面假设要调用 x.f(args)。下面是调用过程的详细描述:

  • 编译时:
    1. 编译器査看对象变量的声明类型和方法名,然后获得所有可能被调用的候选方法。
    2. 编译器査看调用方法时提供的参数类型,然后获得需要调用的方法名字和参数类型。
  • 运行时:
    • 如果方法是 private 方法、static 方法、final 方法或者构造器方法,那么编译器将可以准确地知道应该调用哪个方法,我们将这种调用方式称为静态绑定(static binding)。与此对应的是,调用的方法依赖于对象变量 x 的实际类型,并且在运行时实现动态绑定。
    • 虚拟机预先为每个类创建了一个方法表(method table),方法表中列出了所有方法的签名和实际调用的方法。这样一来,在真正调用方法的时候,虚拟机仅查找方法表就行了。


1、编译器査看对象变量的声明类型和方法名,然后获得所有可能被调用的候选方法。
假设调用 x.f(param),且对象变量 x 被声明为 C 类型。需要注意的是:有可能存在多个名字为 f,但参数类型不一样的方法。例如,可能存在方法 f(int) 和方法 f(String)。编译器将会一一列举所有 C 类中名为 f 的方法和其父类中访问属性为 public 且名为 f 的方法(父类的私有方法不可访问)。至此,编译器已获得所有可能被调用的候选方法。

2、接下来,编译器将査看调用方法时提供的参数类型,然后获得需要调用的方法名字和参数类型。
如果在所有名为 f 的方法中存在一个与提供的参数类型完全匹配,就选择这个方法。这个过程被称为重载解析(overloading resolution)。例如,对于调用 x.f("Hello") 来说,编译器将会挑选 f(String),而不是 f(int)。由于允许类型转换(int 可以转换成 double,Manager 可以转换成 Employee 等),所以这个过程可能很复杂。如果编译器没有找到与参数类型匹配的方法,或者发现经过类型转换后有多个方法与之匹配,就会报告一个错误。至此,编译器已获得需要调用的方法名字和参数类型。

如果方法是 private 方法、static 方法、final 方法或者构造器方法,那么编译器将可以准确地知道应该调用哪个方法,我们将这种调用方式称为静态绑定(static binding)。与此对应的是,调用的方法依赖于对象变量 x 的实际类型,并且在运行时实现动态绑定。在我们列举的示例中,编译器采用动态绑定的方式生成一条调用 f(String) 的指令。

当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与对象变量 x 所引用的对象的实际类型最合适的那个类的方法。假设 x 的实际类型是 D,D 是 C 类的子类。如果 D 类定义了 f(String) 方法,虚拟机就直接调用 D 类的 f(String) 方法;否则(D 类中没有定义 f(String) 方法),将在 D 类的父类中寻找 f(String),以此类推。


每次调用方法都要进行搜索,时间开销相当大。因此,虚拟机预先为每个类创建了一个方法表(method table),方法表中列出了所有方法的签名(方法名、参数类型)和实际调用的方法。这样一来,在真正调用方法的时候,虚拟机仅查找方法表就行了。

在前面的例子中,虚拟机搜索 D 类的方法表,以便寻找与调用 f(Sting) 相匹配的方法。这个方法既有可能是 D.f(String),也有可能是 C.f(String),这里的 C 是 D 的父类。这里需要提醒一点,如果调用 super.f(param),编译器将对对象变量父类的方法表进行搜索。

方法调用的示例

public static void main(String[] args) {
    Employee e = new Manager("Carl Cracker", 80000, 1987, 12, 15);
    System.out.println("salary=" + e.getSalary());
}

现在,查看一下调用 e.getSalary() 的详细过程。对象变量 e 被声明为 Employee 类型。Employee 类只有一个名叫 getSalary() 的方法,这个方法没有参数。因此,在这里不必担心重载解析的问题。

由于 getSalary() 不是 private 方法、static 方法,也不是 final 方法,所以将采用动态绑定。虚拟机为 Employee 和 Manager 两个类生成方法表。


在 Employee 的方法表中,列出了这个类定义的方法。实际上,下面列出的方法并不完整,Employee 类有一个父类 Object,Employee 类从这个父类中还继承了许多方法,在此我们略去了 Object 的方法。

Employee:
    getName() -> Employee.getName()
    getSalary() -> Employee.getSalary()
    getHireDay() -> Employee.getHireDay()
    raiseSalary(double) -> Employee.raiseSalary(doubl e)


Manager 的方法表稍微有些不同。其中有三个方法是继承而来的,一个方法是重新定义的(方法的重写),还有一个方法是新增加的。

Manager:
    getName() -> Employee.getName()
    getSalary() -> Manager.getSalary()
    getHireDay() -> Employee.getHireDay()
    raiseSalary(double) -> Employee.raiseSalary(doubl e)
    setBonus(double) -> Manager.setBonus(double)


在运行时,调用 e.getSalary() 的解析过程为:

  1. 首先,虚拟机提取对象变量 e 的实际类型的方法表。既可能是 Employee、Manager 的方法表,也可能是 Employee 类的其他子类的方法表。
  2. 接下来,虚拟机搜索对象变量 e 的实际类型的方法表。此时,虚拟机已经知道应该调用哪个方法。
  3. 最后,虚拟机调用方法。

动态绑定有一个非常重要的特性:无需对现存的代码进行修改,就可以对程序进行扩展。假设增加一个新类 Executive,并且对象变量 e 有可能引用这个类的对象,我们不需要对包含调用 e.getSalary() 的代码进行重新编译。如果 e 恰好引用一个 Executive 类的对象,就会自动地调用 Executive.getSalary() 方法。

参考资料

《Java核心技术卷一:基础知识》(第10版)第 5 章:继承 5.1.6 理解方法调用

Fast Framework

作者 Mr-zhong

代码改变世界....

一、前言

Fast Framework 基于NET6.0 封装的轻量级 ORM 框架 支持多种数据库 SqlServer Oracle MySql PostgreSql Sqlite

优点: 体积小、可动态切换不同实现类库、原生支持微软特性、流畅API、使用简单、性能高、模型数据绑定采用 委托、强大的表达式解析、支持多种子查询可实现较为复杂查询、源代码可读性强。

缺点:目前仅支持Db Frist Code Frist 后续迭代。

开源地址
https://github.com/China-Mr-zhong/Fast.Framework
(唯一)

目前作者已使用该框架应用多个生产项目

个别亮点功能:数值类型绑定自动格式化小数点后面多余的0、表达式参数名称自动编排 无需强制统一参数名称

本作者不提供任何性能对比数据,请各位感兴趣的自行测试,对比前建议预热连接

二、项目明细
名称 说明
Fast.Framework 框架主项目
Fast.Framework.Logging 基于微软接口实现的文件日志(非必要可不引用)
Fast.Framework.Test 控制台测试项目
Fast.Framework.UnitTest 单元测试项目
Fast.Framework.Web.Test Web测试项目
三、核心对象
  • Ado 原生Ado对象

                    IAdo ado = new AdoProvider(new DbOptions()
                    {
                        DbId = "1",
                        DbType = DbType.MySQL,
                        ProviderName = "MySqlConnector",
                        FactoryName = "MySqlConnector.MySqlConnectorFactory,MySqlConnector",
                        ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
                    });
    
  • DbContext 支持多租户 支持切换不同Ado实现类库 设置 ProviderName和FactoryName 即可

                    IDbContext db = new DbContext(new List<DbOptions>() {
                    new DbOptions()
                    {
                        DbId = "1",
                        DbType = DbType.MySQL,
                        ProviderName = "MySqlConnector",
                        FactoryName = "MySqlConnector.MySqlConnectorFactory,MySqlConnector",
                        ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
                    }});
    
  • DbOptions Json文件配置格式

    "DbOptions": [
        {
          "DbId": 1,
          "DbType": "SQLServer",
          "ProviderName": "System.Data.SqlClient",
          "FactoryName": "System.Data.SqlClient.SqlClientFactory,System.Data",
          "ConnectionStrings": "server=localhost;database=Test;user=sa;pwd=123456789;min pool size=3;max pool size=100;connect timeout=120;"
        }]
    
  • Asp.net Core 依赖注入

    // 注册服务 
    var builder = WebApplication.CreateBuilder(args);
    
    // 添加数据库上下文
    builder.Services.AddFastDbContext();
    
    // 从Json配置文件加载数据库选项
    builder.Services.Configure<List<DbOptions>>(builder.Configuration.GetSection("DbOptions"));
    
    // 产品服务类 通过构造方法注入
    public class ProductService
    {
        /// <summary>
        /// 数据库
        /// </summary>
        private readonly IDbContext db;
    
        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="db">数据库</param>
        public ProductService(IDbContext db)
        {
            this.db = db;
        }
    }
    
四、插入
  • 实体对象插入

                var product = new Product()
                {
                    ProductCode = "1001",
                    ProductName = "测试商品1"
                };
                var result = db.Insert(product).Exceute();
                Console.WriteLine($"实体对象插入 受影响行数 {result}");
    
  • 实体对象插入并返回自增ID 仅支持 SQLServer MySQL SQLite

                var product = new Product()
                {
                    ProductCode = "1001",
                    ProductName = "测试产品1"
                };
                var result = db.Insert(product).ExceuteReturnIdentity();
                Console.WriteLine($"实体对象插入 返回自增ID {result}");
    
  • 实体对象列表插入

                var list = new List<Product>();
                for (int i = 0; i < 2100; i++)
                {
                    list.Add(new Product()
                    {
                        ProductCode = $"编号{i + 1}",
                        ProductName = $"名称{i + 1}"
                    });
                }
                var result = db.Insert(list).Exceute();
                Console.WriteLine($"实体对象列表插入 受影响行数 {result}");
    
  • 匿名对象插入

                var obj = new
                {
                    ProductCode = "1001",
                    ProductName = "测试商品1"
                };
                //注意:需要使用As方法显示指定表名称
                var result = db.Insert(obj).As("Product").Exceute();
                Console.WriteLine($"匿名对象插入 受影响行数 {result}");
    
  • 匿名对象列表插入

                var list = new List<object>();
                for (int i = 0; i < 2100; i++)
                {
                    list.Add(new
                    {
                        ProductCode = $"编号{i + 1}",
                        ProductName = $"名称{i + 1}"
                    });
                }
                //注意:需要使用As方法显示指定表名称
                var result = db.Insert(list).As("Product").Exceute();
                Console.WriteLine($"匿名对象列表插入 受影响行数 {result}");
    
  • 字典插入

                var product = new Dictionary<string, object>()
                {
                    {"ProductCode","1001"},
                    { "ProductName","测试商品1"}
                };
                var result = db.Insert(product).As("Product").Exceute();
                Console.WriteLine($"字典插入 受影响行数 {result}");
    
  • 字典列表插入

                var list = new List<Dictionary<string, object>>();
                for (int i = 0; i < 2100; i++)
                {
                    list.Add(new Dictionary<string, object>()
                    {
                        {"ProductCode","1001"},
                        { "ProductName","测试商品1"}
                     });
                }
                var result = db.Insert(list).As("Product").Exceute();
                Console.WriteLine($"字典列表插入 受影响行数 {result}");
    
五、删除
  • 实体对象删除

                var product = new Product()
                {
                    ProductId = 1,
                    ProductCode = "1001",
                    ProductName = "测试商品1"
                };
                //注意:必须标记KeyAuttribute特性 否则将抛出异常
                var result = db.Delete(product).Exceute();
                Console.WriteLine($"实体删除 受影响行数 {result}");
    
  • 无条件删除

                var result = db.Delete<Product>().Exceute();
                Console.WriteLine($"无条件删除 受影响行数 {result}");
    
  • 表达式删除

                var result = await db.Delete<Product>().Where(w => w.ProductId == 1).ExceuteAsync();
                Console.WriteLine($"条件删除 受影响行数 {result}");
    
  • 特殊删除

    			      //特殊用法 如需单个条件或多个可搭配 WhereColumn或WhereColumns方法
                var result = await db.Delete<object>().As("Product").ExceuteAsync();
                Console.WriteLine($"无实体删除 受影响行数 {result}");
    
六、更新
  • 实体对象更新

                var product = new Product()
                {
                    ProductId = 1,
                    ProductCode = "1001",
                    ProductName = "测试商品1"
                };
                //注意:标记KeyAuttribute特性属性或使用Where条件,为了安全起见全表更新将必须使用Where方法
                var result = db.Update(product).Exceute();
                Console.WriteLine($"对象更新 受影响行数 {result}");
    
  • 指定列更新

                var result = db.Update<Product>(new Product() { ProductCode = "1001", ProductName = "1002" }).Columns("ProductCode", "ProductName").Exceute();
    						// 字段很多的话可以直接new List<string>(){"列1","列2"}
    
  • 忽略列更新

                var result = db.Update<Product>(new Product() { ProductCode = "1001", ProductName = "1002" }).IgnoreColumns("Custom1").Exceute();
                // 同上使用方法一样
    
  • 实体对象列表更新

                var list = new List<Product>();
                for (int i = 0; i < 2022; i++)
                {
                    list.Add(new Product()
                    {
                        ProductCode = $"编号{i + 1}",
                        ProductName = $"名称{i + 1}"
                    });
                }
                //注意:标记KeyAuttribute特性属性或使用WhereColumns方法指定更新条件列
                var result = db.Update(list).Exceute();
                Console.WriteLine($"对象列表更新 受影响行数 {result}");
    
  • 匿名对象更新

                var obj = new
                {
                    ProductId = 1,
                    ProductCode = "1001",
                    ProductName = "测试商品1"
                };
                //注意:需要显示指定表名称 以及更新条件 使用 Where或者WhereColumns方法均可
                var result = db.Update(obj).As("product").WhereColumns("ProductId").Exceute();
                Console.WriteLine($"匿名对象更新 受影响行数 {result}");
    
  • 匿名对象列表更新

                var list = new List<object>();
                for (int i = 0; i < 2022; i++)
                {
                    list.Add(new
                    {
                        ProductId = i + 1,
                        ProductCode = $"编号{i + 1}",
                        ProductName = $"名称{i + 1}"
                    });
                }
                //由于是匿名对象需要显示指定表名称,使用WhereColumns方法指定更新条件列
                var result = db.Update(list).As("Product").WhereColumns("ProductId").Exceute();
                Console.WriteLine($"匿名对象列表更新 受影响行数 {result}");
    
  • 字典更新

                var product = new Dictionary<string, object>()
                {
                    { "ProductId",1},
                    {"ProductCode","1001"},
                    { "ProductName","测试商品1"}
                };
                var result = db.Update(product).As("Product").WhereColumns("ProductId").Exceute();
                Console.WriteLine($"字典更新 受影响行数 {result}");
    
  • 字典列表更新

                var list = new List<Dictionary<string, object>>();
                for (int i = 0; i < 2022; i++)
                {
                    list.Add(new Dictionary<string, object>()
                    {
                        { "ProductId",i+1},
                        {"ProductCode",$"更新编号:{i+1}"},
                        { "ProductName",$"更新商品:{i + 1}"}
                    });
                }
                var result = db.Update(list).As("Product").WhereColumns("ProductId").Exceute();
                Console.WriteLine($"字典列表更新 受影响行数 {result}");
    
  • 指定条件更新

                var product = new Product()
                {
                    ProductId = 1,
                    ProductCode = "1001",
                    ProductName = "测试商品1"
                };
                var result = db.Update(product).Where(p => p.ProductId == 100).Exceute();
                Console.WriteLine($"表达式更新 受影响行数 {result}");
    
  • 并发更新 乐观锁-版本控制

                    //注意:仅支持非列表更新 版本列数据类型仅支持 object、string、Guid 时间类型存在精度丢失所以不做支持
    				var obj = db.Query<Product>().Where(w => w.ProductId == 1).Frist();
                    obj.Custom1 = "测试版本控制修改";
    				//参数为 true 更新失败将抛出异常
                    var result = db.Update(obj).ExceuteWithOptLock(true);
    
七、查询
  • 单一查询

                var data = db.Query<Product>().First();
    
  • 列表查询

                var data = db.Query<Product>().ToList();
    
  • 返回单个字典

                var data = db.Query<Product>().ToDictionary();
    
  • 返回字典列表

                var data = db.Query<Product>().ToDictionaryList();
    
  • 分页查询

                //分页查询不返回总数
                var data = db.Query<Product>().ToPageList(1,100);
                //分页查询返回总数
    			var total = 0;//定义总数变量
    			var data = db.Query<Product>().ToPageList(1, 1, ref total);
                Console.WriteLine($"总数:{total}");
    
  • 计数查询

                var data = db.Query<Product>().Count();
    
  • 任何查询

                var data = db.Query<Product>().Any();
    
  • 条件查询

                var data = db.Query<Product>().Where(w => w.ProductId == 1).ToList;
    
  • Like 查询

                var data = db.Query<Product>().Where(w => w.ProductName.StartsWith("左模糊") || w.ProductName.EndsWith("右模糊") || w.ProductName.Contains("全模糊")).ToList();
    
  • Not Like查询

    var data = db.Query<Product>().Where(w => !w.ProductName.StartsWith("左模糊") || !w.ProductName.EndsWith("右模糊") || !w.ProductName.Contains("全模糊")).ToList();
    
    //由于没有专门去扩展 Not Like 方法,可以用取反或使用比较变通实现 例如 w.ProductName.StartsWith("左模糊")==false 
    //Mysql举例 最终解析后的结果为 `ProductName` Like '%左模糊' = 0 这种用法数据库是支持的 相当于 Not Like
    
  • Select查询 (选择字段)

                var data = db.Query<Product>().Select(s => new
                {
                    s.ProductId,
                    s.ProductName
                }).ToList();
    
  • Select查询 (Case When)

                    var data = db.Query<Product>().Select(s => new
                    {
                        CaseTest1 = SqlFunc.Case(s.Custom1).When("1").Then("xx1").When("2").Then("xx2").Else("xx3").End(),
                        CaseTest2 = SqlFunc.CaseWhen<string>(s.Custom1 == "1").Then("xx1").When(s.Custom1 == "2").Then("xx2").Else("xx3").End()
                    }).ToList();
    
  • 分组查询

                var data = db.Query<Product>().GroupBy(s => new
                {
                    s.ProductId,
                    s.ProductName
                }).ToList();
    
  • 分组聚合查询

                var sql = db.Query<Order>().InnerJoin<OrderDetail>((a, b) => a.OrderId == b.OrderId).GroupBy((a, b) => new
                {
                    a.OrderCode
                }).Select((a, b) => new
                {
                    a.OrderCode,
                    Sum_Qty = SqlFunc.Sum(b.Qty)//支持嵌套
                }).ToList();
    
  • 排序查询

                var data = db.Query<Product>().OrderBy(s => new
                {
                    s.CreateTime
                }).ToList();
                //这是多个字段排序使用方法 还有其它重载方法
    
  • Having查询

                var data = db.Query<Product>().GroupBy(s => new
                {
                    s.ProductId,
                    s.ProductName
                }).Having(s => SqlFunc.Count(s.ProductId) > 1).ToList();
                //必须先使用GroupBy方法 懂得都懂
    
  • 联表查询

                var data = db.Query<Product>().LeftJoin<Class1>((a, b) => a.ProductId == b.ProductId).ToList();
                // 右连接 RightJoin 内连接 InnerJoin 全连接 FullJoin
    
  • 联合查询

                var query1 = db.Query<Product>();
                var query2 = db.Query<Product>();
                db.Union(query1, query2);//联合
                db.UnionAll(query1, query2);//全联合
                //执行查询调用Toxx方法
    
  • 查询并插入 仅支持同实例的数据库 跨库 个人还是建议 用事务分开写查询和插入

                    //方式1
                    var result1 = db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
                    {
                        s.ProductCode,
                        s.ProductName
                    }).Insert<Product>(p => new
                    {
                        p.ProductCode,
                        p.ProductName
                    });
    
                    //方式2 需要注意的是 显示指定不带 列标识符 例如 `列名称1` 如有字段冲突 可自行加上标识符
                    var result2 = db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
                    {
                        s.ProductCode,
                        s.ProductName
                    }).Insert("表名称 同实例不同库 可以使用 db.数据库名称.表名称 ", "列名称1", "列名称2", "`带标识的列名称3`");
    
                    //方式3 需要注意同方式2 一样
                    var result3 = db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
                    {
                        s.ProductCode,
                        s.ProductName
                    }).Insert("表名称 同实例不同库 可以使用 db.数据库名称.表名称 ", new List<string>() { "列名称1" });
    
  • In查询

                    // 方式1
                    var data1 = db.Query<Product>().Where(w => SqlFunc.In(w.ProductCode, "1001", "1002")).ToList();
    
                    // 方式2
                    var data2 = db.Query<Product>().Where(w => SqlFunc.In(w.ProductCode, new List<string>() { "123", "456" })).ToList();
    
                    // 方式3 需要动态更新IN值 使用这种
                    var list = new List<string>() { "123", "456" };
                    var data3 = db.Query<Product>().Where(w => SqlFunc.In(w.ProductCode, list)).ToList();
    
                    // 方法4 参数同上一样 单独分离IN和NotIN 是为了兼容匿名查询
                    var data4 = db.Query<Product>().In("字段名称", "1001", "1002").ToList();
    
  • Select子查询

                    var subQuery = db.Query<Product>().Where(w => w.ProductId == 1).Select(s => s.ProductName);
                    var sql1 = db.Query<Product>().Select(s => new Product()
                    {
                        Custom1 = db.SubQuery<string>(subQuery)// SubQuery 的泛型是根据你左边赋值的属性类型来定义
                    }).ToList();
                    // 这种没有使用new 的 泛型可随意定义 实际作用就是避免 对象属性赋值类型冲突的问题
                    var sql2 = db.Query<Product>().Select(s => db.SubQuery<string>(subQuery)).ToList();
    
  • From子查询

                    var subQuery = db.Query<Product>();
                    var data = db.Query(subQuery).OrderBy(o => o.ProductCode).ToList();
    
  • Join子查询

                    var subQuery = db.Query<Product>();
                    var data = db.Query<Product>().InnerJoin(subQuery, (a, b) => a.ProductId == b.ProductId).ToList();
    
  • Include查询

                    // 联表条件 默认优先匹配主键 其次带有ID结尾的名称
                    var data = db.Query<Category>().Include(i => i.Products).ToList();
    
八、Lambda表达式
  • 动态表达式 命名空间 Fast.Framework.Utils

                    var ex = DynamicWhereExp.Create<Product>().AndIF(1 == 1, a => a.DeleteMark == true).Build();
                    var data = db.Query<Product>().Where(ex).ToList();
    
  • Sql函数 自定义函数 需引入命名空间 Fast.Framework.Utils


    • SqlServer


      • 类型转换


        方法名称 解析示例值 说明 自定义函数
        ToString CONVERT( VARCHAR(255),123) 转换 VARCHAR
        ToDateTime CONVERT( DATETIME,‘2022-09-16’) 转换 DATETIME
        ToDecimal CONVERT( DECIMAL(10,6),‘123’) 转换 DECIMAL
        ToDouble CONVERT( NUMERIC(10,6),‘123’) 转换 NUMERIC
        ToSingle CONVERT( FLOAT,‘123’) 转换 FLOAT
        ToInt32 CONVERT( INT,‘123’) 转换 INT
        ToInt64 CONVERT( BIGINT,‘123’) 转换 BIGINT
        ToBoolean CONVERT( BIT,‘1’) 转换 BIT
        ToChar CONVERT( CHAR(2),'x') 转换 CHAR
      • 聚合函数


        方法名称 解析示例值 说明 自定义函数
        Max MAX( a.[xx] ) 最大值
        Min MIN( a.[xx] ) 最小值
        Count COUNT( a.[xx] ) 计数
        Sum SUM( a.[xx] ) 合计
        Avg AVG( a.[xx] ) 平均
      • 数学函数


        方法名称 解析示例值 说明 自定义函数
        Abs ABS( a.[xx] ) 绝对值
        Round ROUND( a.[xx] ,2 ) 四舍五入
      • 字符串函数


        方法名称 解析示例值 说明 自定义函数
        StartsWith LIKE ‘%’+'xx' 左模糊
        EndsWith LIKE 'xx'+‘%’ 右模糊
        Contains LIKE ‘%’+'xx'+‘%’ 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3) 截取
        Replace REPLACE( 'xxx','x','y') 替换
        Len LEN( 'xxx' ) 长度
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大写
        ToLower LOWER( 'xx' ) 小写
        Operation CreateTime >= @Now_1 日期、数值、字符串范围比较
      • 日期函数


        方法名称 解析示例值 说明 自定义函数
        DateDiff DATEDIFF( DAY ,a.[xx],b.[xx]) 日期相差
        AddYears DATEADD( YEAR,a.[xx],1 ) 添加年份
        AddMonths DATEADD( MONTH,a.[xx],1 ) 添加月份
        AddDays DATEADD( DAY,a.[xx],1 ) 添加天数
        AddHours DATEADD( HOUR,a.[xx],1 ) 添加时
        AddMinutes DATEADD( MINUTE,a.[xx],1 ) 添加分
        AddSeconds DATEADD( SECOND,a.[xx],1 ) 添加秒
        AddMilliseconds DATEADD( MILLISECOND,a.[xx],1 ) 添加毫秒
        Year YEAR( a.[xx] ) 获取年份
        Month MONTH( a.[xx] ) 获取月份
        Day DAY( a.[xx] ) 获取天数
      • 查询函数


        方法名称 解析示例值 说明 自定义函数
        In IN ( a.[xx] ,'x1','x2','x3') In查询
        NotIn NOT IN ( a.[xx] ,'x1','x2','x3') Not In查询
      • 其它函数


        方法名称 解析示例值 说明 自定义函数
        NewGuid NEWID() 获取GUID
        Equals p.[ProductCode] = '123' 比较
        IsNull ISNULL(a.[xx],0) 是否为空
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
    • MySql


      • 类型转换


        方法名称 解析示例值 说明 自定义函数
        ToString CAST( a.`xx` AS CHAR(510) ) 转换 CHAR(510)
        ToDateTime CAST( a.`xx` AS DATETIME ) 转换 DATETIME
        ToDecimal CAST( a.`xx` AS DECIMAL(10,6) ) 转换 DECIMAL(10,6)
        ToDouble CAST( a.`xx` AS DECIMAL(10,6) ) 转换 DECIMAL(10,6)
        ToInt32 CAST( a.`xx` AS DECIMAL(10) ) 转换 DECIMAL(10)
        ToInt64 CAST( a.`xx` AS DECIMAL(19) ) 转换 DECIMAL(19)
        ToBoolean CAST( a.`xx` AS UNSIGNED ) 转换 UNSIGNED
        ToChar CAST( a.`xx` AS CHAR(2) ) 转换 CHAR(2)
      • 聚合函数


        方法名称 解析示例值 说明 自定义函数
        Max MAX( a.`xx` ) 最大值
        Min MIN( a.`xx` ) 最小值
        Count COUNT( a.`xx` ) 计数
        Sum SUM( a.`xx` ) 合计
        Avg AVG( a.`xx` ) 平均
      • 数学函数


        方法名称 解析示例值 说明 自定义函数
        Abs ABS( a.`xx` ) 绝对值
        Round ROUND( a.`xx` ,2 ) 四舍五入
      • 字符串函数


        方法名称 解析示例值 说明 自定义函数
        StartsWith LIKE CONCAT( '%','xx' ) 左模糊
        EndsWith LIKE CONCAT( xx','%' ) 右模糊
        Contains LIKE CONCAT( '%','xx','%' ) 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3 ) 截取
        Replace REPLACE( 'xxx','x','y' ) 替换
        Len LEN( 'xxx' ) 长度
        Trim TRIM( ' xx ' ) 修剪空格
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大写
        ToLower LOWER( 'xx' ) 小写
        Concat CONCAT(a.`xx1`,a.`xx2`) 字符串拼接
      • 日期函数


        方法名称 解析示例值 说明 自定义函数
        DateDiff DATEDIFF( a.`xx`,b.`xx` ) 日期相差 返回相差天数
        TimestampDiff TIMESTAMPDIFF( DAY,a.`xx`,b.`xx` ) 日期相差 指定时间单位
        AddYears DATE_ADD( a.`xx`,INTERVAL 1 YEAR ) 添加年份
        AddMonths DATE_ADD( a.`xx`,INTERVAL 1 MONTH ) 添加月份
        AddDays DATE_ADD( a.`xx`,INTERVAL 1 DAY ) 添加天数
        AddHours DATE_ADD( a.`xx`,INTERVAL 1 HOUR ) 添加时
        AddMinutes DATE_ADD( a.`xx`,INTERVAL 1 MINUTE ) 添加分
        AddSeconds DATE_ADD( a.`xx`,INTERVAL 1 SECOND ) 添加秒
        AddMilliseconds DATE_ADD( a.`xx`,INTERVAL 1 MINUTE_SECOND ) 添加毫秒
        Year YEAR( a.`xx` ) 获取年份
        Month MONTH( a.`xx` ) 获取月份
        Day DAY( a.`xx` ) 获取天数
      • 查询函数


        方法名称 解析示例值 说明 自定义函数
        In IN ( a.`xx` ,'x1','x2','x3' ) In查询
        NotIn NOT IN ( a.`xx` ,'x1','x2','x3' ) Not In查询
      • 其它函数


        方法名称 解析示例值 说明 自定义函数
        NewGuid UUID() 获取GUID
        Equals p.`ProductCode` = '123' 比较
        IfNull IFNULL( a.`xx`,0 ) 如果为空
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
    • Oracle


      • 类型转换


        方法名称 解析示例值 说明 自定义函数
        ToString CAST( a."xx" AS VARCHAR(255) ) 转换 VARCHAR
        ToDateTime TO_TIMESTAMP( a."xx" ,'yyyy-MM-dd hh:mi:ss.ff') 转换 DATETIME
        ToDecimal CAST( a."xx" AS DECIMAL(10,6) ) 转换 DECIMAL
        ToDouble CAST( a."xx" AS NUMBER ) 转换 NUMBER
        ToSingle CAST( a."xx" AS FLOAT ) 转换 FLOAT
        ToInt32 CAST( a."xx" AS INT ) 转换 INT
        ToInt64 CAST( a."xx" AS NUMBER ) 转换 NUMBER
        ToBoolean CAST( a."xx" AS CHAR(1) ) 转换 CHAR
        ToChar CAST( a."xx" AS CHAR(2) ) 转换 CHAR
      • 聚合函数


        方法名称 解析示例值 说明 自定义函数
        Max MAX( a."xx" ) 最大值
        Min MIN( a."xx" ) 最小值
        Count COUNT( a."xx" ) 计数
        Sum SUM( a."xx" ) 合计
        Avg AVG( a."xx" ) 平均
      • 数学函数


        方法名称 解析示例值 说明 自定义函数
        Abs ABS( a."xx" ) 绝对值
        Round ROUND( a."xx" ,2 ) 四舍五入
      • 字符串函数


        方法名称 解析示例值 说明 自定义函数
        StartsWith LIKE CONCAT( '%','xx' ) 左模糊
        EndsWith LIKE CONCAT( 'xx','%' ) 右模糊
        Contains LIKE CONCAT( '%','xx','%' ) 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3) 截取
        Replace REPLACE( 'xxx','x','y') 替换
        Length LENGTH( 'xxx' ) 长度
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大写
        ToLower LOWER( 'xx' ) 小写
        Concat CONCAT(a."xx1",a."xx2") 字符串拼接
      • 日期函数


        方法名称 解析示例值 说明 自定义函数
        Year EXTRACT( YEAR FROM a."xx" ) 获取年份
        Month EXTRACT( MONTH FROM a."xx" ) 获取月份
        Day EXTRACT( DAY FROM a."xx" ) 获取天数
      • 查询函数


        方法名称 解析示例值 说明 自定义函数
        In IN ( a."xx" ,'x1','x2','x3') In查询
        NotIn NOT IN ( a."xx",'x1','x2','x3') Not In查询
      • 其它函数


        方法名称 解析示例值 说明 自定义函数
        Equals p."ProductCode" = '123' 比较
        Nvl NVL( a."xx",0 ) 空,默认
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
    • PostgreSql


      • 类型转换


        方法名称 解析示例值 说明 自定义函数
        ToString a."xx"::VARCHAR(255) 转换 VARCHAR
        ToDateTime a."xx"::TIMESTAMP 转换 TIMESTAMP
        ToDecimal a."xx"::DECIMAL(10,6) 转换 DECIMAL
        ToDouble a."xx"::NUMERIC(10,6) 转换 NUMERIC
        ToSingle a."xx"::REAL 转换 REAL
        ToInt32 a."xx"::INTEGER 转换 INT
        ToInt64 a."xx"::BIGINT 转换 BIGINT
        ToBoolean a."xx"::BOOLEAN 转换 BOOLEAN
        ToChar a."xx"::CHAR(2) 转换 CHAR
      • 聚合函数


        方法名称 解析示例值 说明 自定义函数
        Max MAX( a."xx" ) 最大值
        Min MIN( a."xx" ) 最小值
        Count COUNT( a."xx" ) 计数
        Sum SUM( a."xx" ) 合计
        Avg AVG( a."xx" ) 平均
      • 数学函数


        方法名称 解析示例值 说明 自定义函数
        Abs ABS( a."xx" ) 绝对值
        Round ROUND( a."xx" ,2 ) 四舍五入
      • 字符串函数


        方法名称 解析示例值 说明 自定义函数
        StartsWith LIKE CONCAT( '%','xx' ) 左模糊
        EndsWith LIKE CONCAT( 'xx','%' ) 右模糊
        Contains LIKE CONCAT( '%','xx','%' ) 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3 ) 截取
        Replace REPLACE( 'xxx','x','y' ) 替换
        Length LENGTH( 'xxx' ) 长度
        Trim TRIM( ' xx ' ) 修剪空格
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大写
        ToLower LOWER( 'xx' ) 小写
        Concat CONCAT(a."xx1",a."xx2") 字符串拼接
      • 日期函数


        方法名称 解析示例值 说明 自定义函数
        AddYears a."xx" + INTERVAL '1 YEAR' 添加年份
        AddMonths a."xx" + INTERVAL '1 MONTH' 添加月份
        AddDays a."xx" + INTERVAL '1 DAY' 添加天数
        AddHours a."xx" + INTERVAL '1 HOUR' 添加时
        AddMinutes a."xx" + INTERVAL '1 MINUTE' 添加分
        AddSeconds a."xx" + INTERVAL '1 SECOND' 添加秒
        AddMilliseconds a."xx" + INTERVAL '1 MINUTE_SECOND' 添加毫秒
        Year YEAR( a."xx" ) 获取年份
        Month MONTH( a."xx" ) 获取月份
        Day DAY( a."xx" ) 获取天数
      • 查询函数


        方法名称 解析示例值 说明 自定义函数
        In IN ( a."xx" ,'x1','x2','x3' ) In查询
        NotIn NOT IN ( a."xx" ,'x1','x2','x3' ) Not In查询
      • 其它函数


        方法名称 解析示例值 说明 自定义函数
        Equals p.”ProductCode“ = '123' 比较
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
    • Sqlite


      • 类型转换


        方法名称 解析示例值 说明 自定义函数
        ToString CAST(a.[xx] AS TEXT ) 转换 TEXT
        ToDateTime DATETIME( a.[xx] ) 转换 DateTime
        ToDecimal CAST(a.[xx] AS DECIMAL(10,6) ) 转换 DECIMAL
        ToDouble CAST(a.[xx] AS NUMERIC(10,6) ) 转换 NUMERIC
        ToSingle CAST(a.[xx] AS FLOAT ) 转换 FLOAT
        ToInt32 CAST(a.[xx] AS INTEGER ) 转换 INTEGER
        ToInt64 CAST(a.[xx] AS BIGINT ) 转换 BIGINT
        ToBoolean CAST(a.[xx] AS CHAR(1) ) 转换 CHAR
        ToChar CAST(a.[xx] AS CHAR(2) ) 转换 CHAR
      • 聚合函数


        方法名称 解析示例值 说明 自定义函数
        Max MAX( a.[xx] ) 最大值
        Min MIN( a.[xx] ) 最小值
        Count COUNT( a.[xx] ) 计数
        Sum SUM( a.[xx] ) 合计
        Avg AVG( a.[xx] ) 平均
      • 数学函数


        方法名称 解析示例值 说明 自定义函数
        Abs ABS( a.[xx] ) 绝对值
        Round ROUND( a.[xx] ,2 ) 四舍五入
      • 字符串函数


        方法名称 解析示例值 说明 自定义函数
        StartsWith LIKE '%'||'xx' 左模糊
        EndsWith LIKE 'xx'||'%' 右模糊
        Contains LIKE '%'||'xx'||'%' 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3 ) 截取
        Replace REPLACE( 'xxx','x','y' ) 替换
        Length LENGTH( 'xxx' ) 长度
        Trim TRIM( ' xx ' ) 修剪空格
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大写
        ToLower LOWER( 'xx' ) 小写
      • 日期函数


        方法名称 解析示例值 说明 自定义函数
        AddYears DATETIME( a.[xx],'1 YEAR' ) 添加年份
        AddMonths DATETIME( a.[xx],'1 MONTH' ) 添加月份
        AddDays DATETIME( a.[xx],'1 DAY' ) 添加天数
        AddHours DATETIME( a.[xx],'1 HOUR' ) 添加时
        AddMinutes DATETIME( a.[xx],'1 MINUTE' ) 添加分
        AddSeconds DATETIME( a.[xx],'1 SECOND' ) 添加秒
        AddMilliseconds DATETIME( a.[xx],'1 YEAR' ) 添加毫秒
        Year STRFTIME( '%Y', a.[xx] ) 获取年份
        Month STRFTIME( '%m', a.[xx] ) 获取月份
        Day STRFTIME( '%j', a.[xx] ) 获取天数
      • 查询函数


        方法名称 解析示例值 说明 自定义函数
        In IN ( a."xx" ,'x1','x2','x3' ) In查询
        NotIn NOT IN ( a."xx" ,'x1','x2','x3' ) Not In查询
      • 其它函数


        方法名称 解析示例值 说明 自定义函数
        Equals p.”ProductCode“ = '123' 比较
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
  • 添加自定义函数解析

                    //注意:只能扩展未实现的方法名称 不能覆盖原有的实现
                    Models.DbType.MySQL.AddSqlFunc("方法名称", (visit, method, sqlStack) =>
                    {
                        //解析逻辑
                    });
    
九、数据库日志
				        db.Aop.DbLog = (sql, dp) =>
                {
                    Console.WriteLine($"执行Sql:{sql}");
                    if (dp != null)
                    {
                        foreach (var item in dp)
                        {
                            Console.WriteLine($"参数名称:{item.ParameterName} 参数值:{item.ParameterValue}");
                        }
                    }
                };
十、事务
  • 普通事务

                  try
                  {
                      db.Ado.BeginTran();//开启事务
    
                      // 执行 CRUD
    
                      db.Ado.CommitTran();//提交事务
                  }
                  catch (Exception ex)
                  {
                      Console.WriteLine(ex.Message);
                      db.Ado.RollbackTran();//回滚事务
                  }
    
  • 更大范围的事务

                    try
                    {
                        db.BeginTran();//开启事务
    
                      	// 执行 CRUD
    
                        db.CommitTran();//提交事务
                    }
                    catch (Exception ex)
                    {
                        db.RollbackTran();//回滚事务
                        Console.WriteLine(ex.Message);
                    }
    
十一、多租户
  • 改变数据库

                    //数据库配置可从Json配置文件加载
    				IDbContext db = new DbContext(new List<DbOptions>() {
                    new DbOptions()
                    {
                        DbId = "1",
                        DbType = Models.DbType.SQLServer,
                        ProviderName = "System.Data.SqlClient",
                        FactoryName = "System.Data.SqlClient.SqlClientFactory,System.Data",
                        ConnectionStrings = "server=localhost;database=Test;user=sa;pwd=123456789;min pool size=0;max pool size=100;connect timeout=120;"
                    },
                    new DbOptions()
                    {
                        DbId = "2",
                        DbType = Models.DbType.MySQL,
                        ProviderName = "MySqlConnector",
                        FactoryName = "MySqlConnector.MySqlConnectorFactory,MySqlConnector",
                        ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=0;max pool size=100;connect timeout=120;"
                    }});
                    db.ChangeDb("2");//切换到MySQL
    
十二、原生特性支持
    /// <summary>
    /// 产品
    /// </summary>
    [Table("ProductMain")]
    public class Product
    {
        /// <summary>
        /// 产品ID
        /// </summary>
        [Key]
        public int ProductId { get; set; }

        /// <summary>
        /// 产品编号
        /// </summary>
        [Column("ProductCode")]//不标记默认取当前属性名称
        public string ProductCode { get; set; }

        /// <summary>
        /// 自定义1
        /// </summary>
        [NotMapped]
        public string Custom1 { get; set; }
    }
十三、原生Ado
                // 原始起步
                // var conn = db.Ado.DbProviderFactory.CreateConnection();
                // var cmd = conn.CreateCommand();

                // 封装的方法分别以Execute和Create开头以及预处理 PrepareCommand 方法
                // 该方法可以自动帮你处理执行的预操作,主要作用是代码复用。

                // 当有非常复杂的查询 ORM不能满足需求的时候可以使用原生Ado满足业务需求

                // 构建数据集核心扩展方法 分别有 FristBuild ListBuild DictionaryBuild DictionaryListBuild
                var data = db.Ado.ExecuteReader(CommandType.Text, "select * from product", null).ListBuild<Product>();
十四、工作单元
  • 注册数据库上下文和工作单元服务

    var builder = WebApplication.CreateBuilder(args);
    
    var configuration = builder.Configuration;
    
    // 添加数据库上下文服务
    builder.Services.AddFastDbContext();
    // 添加工作单元服务
    builder.Services.AddFastUnitOfWork();
    
    // 加载数据库配置
    builder.Services.Configure<List<DbOptions>>(configuration.GetSection("DbConfig"));
    
  • 实际应用

            /// <summary>
            /// 工作单元
            /// </summary>
            private readonly IUnitOfWork unitOfWork;
    
    
            /// <summary>
            /// 构造方法
            /// </summary>
            /// <param name="unitOfWork">工作单元</param>
            public UnitOfWorkTestService(IUnitOfWork unitOfWork)
            {
                this.unitOfWork = unitOfWork;
            }
    
            /// <summary>
            /// 测试
            /// </summary>
            /// <returns></returns>
            public string Test()
            {
                //unitOfWork 对象无需显示使用using
                var result1 = unitOfWork.Db.Insert(new Category()
                {
                    CategoryName = "类别3"
                }).Exceute();
    
                var result2 = unitOfWork.Db.Insert(new Product()
                {
                    ProductCode = "测试工作单元",
                }).Exceute();
    
                unitOfWork.Commit();
    
                return "工作单元执行完成...";
            }
    
十五、大数据导入
  • 批复制 仅支持SqlServer Oracle MySql PostgreSql

                    var list = new List<Product>();
                    for (int j = 1; j <= 100000; j++)
                    {
                        list.Add(new Product()
                        {
                            CategoryId = 1,
                            ProductCode = $"测试编号_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            ProductName = $"测试名称_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            CreateTime = DateTime.Now,
                            Custom1 = $"测试自定义1_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom2 = $"测试自定义2_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom3 = $"测试自定义3_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom4 = $"测试自定义4_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom5 = $"测试自定义5_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom6 = $"测试自定义6_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom7 = $"测试自定义7_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom8 = $"测试自定义8_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom9 = $"测试自定义9_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom10 = $"测试自定义10_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom11 = $"测试自定义11_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom12 = $"测试自定义12_{Timestamp.CurrentTimestampSeconds()}_{j}",
                        });
                    }
                    db.Fast<Product>().BulkCopy(list);
    

还是先举个例子,魂斗罗小游戏应该很多90后都玩过,那个时代没有Iphone,没有各种电子产品(小学初中时代),这种小游戏应该就是很多90后的青春,反正那个时候只要放假就白天黑夜得玩。它就是那种2个好基友边玩边捡各种装备的游戏,越玩的远捡的装备越好,玩得越高兴。而MyBatis-Plus就类似Mybatis的的另一个好基友,那真是太好用了。基友搭配,效率翻倍。

而如果你项目中使用了MyBatis-Plus,那这个脚手架我一定要推荐给你,它就类似给2个基友加上了辅助装备,游戏起飞了。而如果你项目中也使用了这个脚手架那开发应该也是飞一般的感觉【耶,我这前面貌似一下子给很多品牌打了广告】。

好了不卖关子了这个脚手架就是(
renren-generator
),它是人人开源做的一个脚手架地址如下:

https://gitee.com/renrenio/renren-generator

使用它也是很简单

本地部署

  • 通过git下载源码

  • 修改application.yml,更新MySQL账号和密码、数据库名称

  • Eclipse、IDEA运行RenrenApplication.java,则可启动项目

  • 项目访问路径:http://localhost:8080

改配置也就改如下配置就行了:

运行的效果如下:

我的Student库中只有一张User表,然后就显示出来如上图所示,然后你可以选中相应的表生成代码结构如下,在线生成entity、xml、dao、service代码。

里面的代码结构我就不全部打开了,打开部分如下(剩余的大家可以自己运行项目打开看看):

@Mapperpublic interface UserDao extends BaseMapper<UserEntity>{

}
@Data
@TableName(
"user")public class UserEntity implementsSerializable {private static final long serialVersionUID = 1L;


@TableId
privateLong personId;privateString personName;

}

可以说这些生成的代码可以和MyBatis-Plus神配合。

好了然后就把renren-generator仔细讲一下。

1:首先看配置文件,指定使用哪种数据库,不多说一切都在图中(哈!我只听说过,一切都在酒中)。

2:看generator.properties文件。

一般项目进行如上的更改就能如行云流水般使用了。但是我要让你对他全部掌握,那就继续讲。

它是怎样生成我们开发中需要的各种文件的呢?

1:看它的maper文件夹下的文件,找一个例(MySQLGeneratorDao.xml)

2:实体类生成规则。

3:各种代码生成规则的Util是这个GenUtils类,这个类里作者写了很多中文注释,就不带大家看了,自己下载看一下就可以了。

希望这篇文章对你开发有利,学会使用这个脚手架能提高你的开发效率,这是开源的项目欢迎你分享,转发,让更多的人知道这个项目。

对了你首先也要掌握MyBatis-Plus的用法哦,这是中国人自己写的,很容易理解附上网址,欢迎你去学习很是简单。为简化开发而存在的项目

https://baomidou.com/

楔子

最近和一家公司在谈一个项目合作,他们公司主要是做电力相关的。 项目背景大概是这样的:
国家电网对电网资产需要做到数字化管理,对现有变压器台区内的电表箱电能表做可视化数字孪生管理。
由于涉及到的台区非常多,所以客户希望开发的不是单个项目,而是可以实现项目的3D编辑器,使得电网的台区经理使用编辑器编辑出所负责的变压器台区的设备关系场景及数据状态展示。

三维可视化方面,三维组态,我们经验还是挺多的,比如数据中心、医院、学校等三维可视化项目,还包括智慧园区、智慧城市、智慧小镇的方向的等三维可视化。
下面先上几张三维可视化的图瞅瞅:
image.png
image.png
image.png
客户需要的是一个布局工具,而不是直接的三维场景,这比直接搭建一个三维的场景要难许多。
但是所谓万事开头难,难在不开头。 天下事有难易乎,干就是了。由于之前做过油田的三维布局,虽然内容上不太一样,但是技术上是类似的,还是相对来说容易很多。
在商务人员和客户确立合同,正式立项后, 我们的设计小姐姐,开发小哥哥,建模小弟弟,都各司其职,下边就讲一下项目的大概内容。

创建模型库

模型库主要包括了园区模型(通用),楼宇模型(通用),台区模型,电表箱,电能表等等。
image.png
image.png
首先是开发同事搭建一个模型库的功能,主要包括了对于模型的管理。 模型库功能主要包括,模型的上传,预览和分类和列表功能。同时让建模小伙把相关模型使用3D建模工具 3d max或者c4d 进行模型的建模。建模后,导出后缀为obj/gltf/fbx格式文件,建模后的所有模型文件,最终会上传到模型库,模型库的管理目录如下图所示:
image.png

三维编辑功能

三维编辑能力是电力布局三维编辑器的核心功能。

生成模型

三维编辑能力之一是把模型列表的功能拖拽到三维画布上,生成三维模型。主要的技术实现包括了 DragAndDrop和模型加载:
其中drag and drop 大致如下:

function dragstart_handler(ev) {
  ev.dataTransfer.setData("model","./xxx.gltf");
}

function dragover_handler(ev) {
  ev.preventDefault();
  ev.dataTransfer.dropEffect = "move";
}
function drop_handler(ev) {
  ev.preventDefault();
  var data = ev.dataTransfer.getData("model");
  let model = ModelLoader.load(data);
  ...
}

而模型加载主要使用了GLTFLoader,大致如下

loader.load(modelPath, function (gltf) {
  // todo 
  // add gltf to scene
}

场景编辑

模型拖入场景中后,还可以在场景中二次编辑模型的位置,大小和其他属性。 可以通过属性框设置属性,也可以通过gizmo工具进行平移,旋转和缩放模型。如下图所示:
image.png
image.png
image.png
image.png

当然还有更多的底层能力,包括undo,redo,批量生产,批量布局,打组,解散打组,拖拽复制能力,批量移动,旋转和缩放等等能力,都是用于易用性的开发,此处不在详细说明。后面将会有文章专门说明编辑器的底层能力。

动态楼层

客户需要能够动态生产楼层,楼层模型拉入场景时,手动输入 地上层数,地下层数,单元数,然后按照输入的层数自动生成相应楼宇模型。楼层支持动态修改楼层数和单元数量。这样可以达到的目的就是通用型,不用针对每个台区进行楼层的建模,减少后期的工作量。
626f7e815295ff07f66ffd42d372f79.png
bab51f8ed71f808ac8cdecc9bcb3b57.png
f2f0dc2ac1ed0ba4bdad9a7f771f84a.png

通过对于楼层+ 楼顶进行分开建模,然后把楼层进行组合的能力,来实现上述功能。

连线功能

在台区和电表之间要能够添加连线,表示联通关系,效果如下所示:
image.png
输入台区模型编号和电表模型编号。 然后按照横平竖直的方法连接连线。其中由于webgl的line宽度只能为1,影响效果,所以我们的连线使用了自己封装的Line,类似threejs的MeshLine,通过Mesh来模拟Line,可以指定line的宽度。代码如下所示:

 const material = new dt.MeshLineMaterial({
    useMap: true,
    map: texture,
    color: new dt.Color("red"),
    transparent: true,
    clipRatio: .1,
    opacity: 1,
    depthTest: false,
    // depthWrite: false,
    resolution: new dt.Vec2(graph.width, graph.height),
    sizeAttenuation: false,
    lineWidth: 100,
    repeat: new dt.Vec2(20, 3),
    offset: new dt.Vec2(0, 0),
    gradientStop: [0, 0.2, random(0.55, 0.65), 1],
    gradientColor: ["blue", "green", "orange", "red"],
    blending: dt.AddtiveBlending,
  });
  var g = new dt.MeshLine();
  g.setGeometry(geo);
  var mesh = new dt.Mesh(g.geometry, material);

特效

通过平台的脚步能力,可以实现二次开发的能力,二次开发实现的特效如下:

  • 线条的流动效果

    通过uv流动动画+特定的贴图,可以实现线条的流动效果,比如demo效果如下:
    image.png
    有关uv流动动画的原理,可以参考笔者之前的文章《
    》。

  • 台区、电能表模型轮廓发光的呼吸灯闪烁效果

    通过引擎的OutlineRenderer,可以实现模型的轮廓效果,并且通过OutlineRenderer的参数的不断修改,便可以实现呼吸灯的闪烁的效果,代码如下所示:

graph.outlineMethod = "glow";
if (graph._outlineRenderer) {
  let pass = graph._outlineRenderer.outlinePass;
  pass.visibleEdgeColor = new Color(controls.visibleEdgeColor);
  pass.hiddenEdgeColor = new Color(controls.visibleEdgeColor);
  pass.edgeGlow = controls.edgeGlow;
  pass.edgeThickness = controls.edgeThickness;
  pass.edgeStrength = controls.edgeStrength;
  pass.downSampleRatio = parseInt(controls.downSampleRatio);
}

最终的效果如下图所示:
image.png

总结

本文主要阐述了电力行业三维布局的主要功能特点。

最后,关注公号“ITMan彪叔” 可以添加作者微信进行交流,及时收到更多有价值的文章。

简单学懂链式编程

一句话定义

链式编程是一种编程风格,它允许在
同一个对象
上通过
多个方法
的调用链实现一系列操作,从而简化代码,提高可读性,和代码的可维护性。

一个流程看懂

创建对象
->
连续调用对象方法
->
返回对象本身
->
使用对象方法获取属性或执行其他操作

示例

public class Person {
    private String name;
    private int age;
    private String gender;

    public Person setName(String name) {
        this.name = name;
        return this;//返回this对象
    }

    public Person setAge(int age) {
        this.age = age;
        return this;
    }

    public Person setGender(String gender) {
        this.gender = gender;
        return this;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getGender() {
        return gender;
    }

    public static void main(String[] args) {
        // 链式编程
        Person person = new Person()
                .setName("张三")
                .setAge(20)
                .setGender("男");

        // 调用对象方法
        String name = person.getName();
        int age = person.getAge();
        String gender = person.getGender();

        // 输出结果
        System.out.println("姓名:" + name);
        System.out.println("年龄:" + age);
        System.out.println("性别:" + gender);
    }
}

在上面的代码中,我们

  1. 创建了一个
    Person
    类,并定义了
    setName()

    setAge()

    setGender()
    方法来设置对象的属性,并返回
    Person
    对象本身,以便实现方法调用链。
  2. 然后,我们使用链式编程的方式创建一个
    Person
    对象并连续调用
    setName()

    setAge()

    setGender()
    方法来设置对象属性,最终返回了一个已经设置好属性的
    Person
    对象。
  3. 最后,我们调用
    getName()

    getAge()

    getGender()
    方法获取对象的属性并输出。

通过这个示例,可以看到链式编程的基本流程是:创建对象 -> 连续调用对象方法 -> 返回对象本身 -> 使用对象方法获取属性或执行其他操作。