当前位置: 首页 > news >正文

【EF Core】再谈普通实体关系与 Owned 关系的区别

在很多个世纪前,老周曾写过实体之间普通关系(一对一,一对多,多对多)与 Owned 关系的区别。不过,那次写得比较粗浅,逼格不够高,于是,老周厚着脸皮地决定重新写一下。

首先,为什么这次老周用原单词 Owned 呢,官方文档目前的翻译(怀疑是机器干的)为“从属”,这种说法与普通关系数据库中一对多、多对多等关系描述不太 好区分。其实老周觉得应该把 Owned 翻译为“独占”关系——你完全属于我的。普通关系中的厕所是公共厕所,我可以用,邻居A、B、C也可以用;而 Owned 关系中的厕所是私人的,我用我家的厕所,A用A家自己的厕所,B不能用A家的厕所。

这种玩意儿比某少年马戏团的粉丝还抽象,要理解最好的方法是比较。本文老周就对这两类关系做一轮大比拼。

One and One

首先我们来看“一”和“一”的方式。为了保持数据结构的一致,咱们用这三个实体来实验。

public class HardwareInfo
{public int HwID { get; set; }               // 主键public long MemorySize { get; set; }        // 内存大小public int HarddiskNum { get; set; }        // 硬盘数量public long HDDSize { get; set; }           // 硬盘大小public bool InteGrp { get; set; }           // 是否有集显
}public class Desktop
{public int ID { get; set; }                 // 主键public HardwareInfo HWInfo { get; set; }    // 硬件信息
}public class Laptop
{public int ID { get; set; }                  // 主键public HardwareInfo HWInfo { get; set; }     // 硬件信息
}

HardwareInfo 表示硬件参数,不管是台式机(Desktop)还是笔记本(Laptop)都可以共用这样的数据结构。

先定义用在普通关系的上下文类——MyContextR,R结尾表示 Relational。

public class MyContextR : DbContext
{public DbSet<Desktop> PCs { get; set; }public DbSet<Laptop> Laps { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){optionsBuilder.UseSqlServer(@"server=<你的服务器>;database=rdb").LogTo(m => Debug.WriteLine(m));}protected override void OnModelCreating(ModelBuilder mb){// 配置主键mb.Entity<HardwareInfo>().HasKey(m => m.HwID);mb.Entity<Laptop>(ent =>{ent.HasKey(k => k.ID);ent.HasOne(x => x.HWInfo);});mb.Entity<Desktop>(eb =>{eb.HasKey(a => a.ID);eb.HasOne(y => y.HWInfo);});}
}

由于老周在定义实体类时“粗心大意”,主键属性的命名无法让 EF Core 自动识别,所以要在 OnModelCreating 方法中显式配置一下。注意,HasOne 让它们建立一对一的关系,即PC有一个HardwareInfo 实例,笔记本也有。

第二个上下文类是面向“独占”关系的 MyContextO,O 结尾表示 Owned。

public class MyContextO : DbContext
{public DbSet<Laptop> Laps { get; set; }public DbSet<Desktop> PCs { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){optionsBuilder.UseSqlServer(@"server=<你的服务器>;database=odb").LogTo(g => Debug.WriteLine(g));}protected override void OnModelCreating(ModelBuilder mb){mb.Entity<Laptop>().HasKey(m => m.ID);mb.Entity<Desktop>().HasKey(n => n.ID);mb.Entity<Laptop>().OwnsOne(x => x.HWInfo);mb.Entity<Desktop>().OwnsOne(w => w.HWInfo);}
}

OwnsOne 表示一占一,PC占用一个HardwareInfo实例,笔记本也占用一个,两者不相干。这种情形 HardwareInfo 是不需要主键的,为什么?往下看你就懂了。

咱们依次实例化这两个上下文对象,然后让它自己创建数据库。

static void Main(string[] args)
{using MyContextR c1 = new();c1.Database.EnsureCreated();using MyContextO c2 = new();c2.Database.EnsureCreated();
}

实验结果发现,普通一对一关系中,创建了三个表:

CREATE TABLE [HardwareInfo] ([HwID] int NOT NULL IDENTITY,[MemorySize] bigint NOT NULL,[HarddiskNum] int NOT NULL,[HDDSize] bigint NOT NULL,[InteGrp] bit NOT NULL,CONSTRAINT [PK_HardwareInfo] PRIMARY KEY ([HwID]);CREATE TABLE [Laps] ([ID] int NOT NULL IDENTITY,[HWInfoHwID] int NULL,CONSTRAINT [PK_Laps] PRIMARY KEY ([ID]),CONSTRAINT [FK_Laps_HardwareInfo_HWInfoHwID] FOREIGN KEY ([HWInfoHwID]) REFERENCES [HardwareInfo] ([HwID])
);CREATE TABLE [PCs] ([ID] int NOT NULL IDENTITY,[HWInfoHwID] int NULL,CONSTRAINT [PK_PCs] PRIMARY KEY ([ID]),CONSTRAINT [FK_PCs_HardwareInfo_HWInfoHwID] FOREIGN KEY ([HWInfoHwID]) REFERENCES [HardwareInfo] ([HwID])
);

EF Core 这货还挺聪明的,把外键分别放在 Desktop 和 Laptop 中,这样可避免在 HardwareInfo 中出现两个外键,不好约束。毕竟这是一对一关系,外键放在哪一端都可以。

然后看看“独占”关系中的一对一,它创建了两个表:

CREATE TABLE [Laps] ([ID] int NOT NULL IDENTITY,[HWInfo_HwID] int NULL,[HWInfo_MemorySize] bigint NULL,[HWInfo_HarddiskNum] int NULL,[HWInfo_HDDSize] bigint NULL,[HWInfo_InteGrp] bit NULL,CONSTRAINT [PK_Laps] PRIMARY KEY ([ID])
);CREATE TABLE [PCs] ([ID] int NOT NULL IDENTITY,[HWInfo_HwID] int NULL,[HWInfo_MemorySize] bigint NULL,[HWInfo_HarddiskNum] int NULL,[HWInfo_HDDSize] bigint NULL,[HWInfo_InteGrp] bit NULL,CONSTRAINT [PK_PCs] PRIMARY KEY ([ID])
);

你没看错,只有两个表,HardwareInfo 直接被拆开了,Desktop和Laptop各拥有一份。现在你明白了吧,为什么 HardwareInfo 在这种关系下不需要主键,因为它们不独成表。

那么,如果让 HardwareInfo 独立建表呢,又会怎样?咱们把 MyContextO 类的代码改一下,为 HardwareInfo 类单独建表。

public class MyContextO : DbContext
{public DbSet<Laptop> Laps { get; set; }public DbSet<Desktop> PCs { get; set; }……protected override void OnModelCreating(ModelBuilder mb){mb.Entity<Desktop>(et =>{et.HasKey(a => a.ID);et.OwnsOne(b => b.HWInfo, ob =>{ob.ToTable("Desktop_HW");ob.WithOwner();});});mb.Entity<Laptop>(et =>{et.HasKey(a => a.ID);et.OwnsOne(m => m.HWInfo, ob =>{ob.ToTable("Laptop_HW");ob.WithOwner();});});}
}

这个地方,WithOwner 方法可以不调用,因为 HardwareInfo 类没有定义指向 Laptop 或 Desktop 的反向导航属性。

这一次,会创建四个表:

CREATE TABLE [Desktop_HW] ([DesktopID] int NOT NULL,[HwID] int NOT NULL,[MemorySize] bigint NOT NULL,[HarddiskNum] int NOT NULL,[HDDSize] bigint NOT NULL,[InteGrp] bit NOT NULL,CONSTRAINT [PK_Desktop_HW] PRIMARY KEY ([DesktopID]),CONSTRAINT [FK_Desktop_HW_PCs_DesktopID] FOREIGN KEY ([DesktopID]) REFERENCES [PCs] ([ID]) ON DELETE CASCADE
);CREATE TABLE [Laptop_HW] ([LaptopID] int NOT NULL,[HwID] int NOT NULL,[MemorySize] bigint NOT NULL,[HarddiskNum] int NOT NULL,[HDDSize] bigint NOT NULL,[InteGrp] bit NOT NULL,CONSTRAINT [PK_Laptop_HW] PRIMARY KEY ([LaptopID]),CONSTRAINT [FK_Laptop_HW_Laps_LaptopID] FOREIGN KEY ([LaptopID]) REFERENCES [Laps] ([ID]) ON DELETE CASCADE
);CREATE TABLE [PCs] ([ID] int NOT NULL IDENTITY,CONSTRAINT [PK_PCs] PRIMARY KEY ([ID])
);CREATE TABLE [Laps] ([ID] int NOT NULL IDENTITY,CONSTRAINT [PK_Laps] PRIMARY KEY ([ID])
);

EF Core 很有才,咱们没有为 HardwareInfo 定义主键,于是它自己生成了,在 Laptop_HW 表中生成 LaptopID 列作为主键,同时也作为外键,引用 Laptop.ID;在 Desktop_HW 表中生成了 DesktopID 列作为主键,同时作为外键,引用 Desktop.ID。

还要补充解释一下模型配置代码。

 mb.Entity<Laptop>(et =>{et.HasKey(a => a.ID);et.OwnsOne(m => m.HWInfo, ob =>{ob.ToTable("Laptop_HW");//ob.WithOwner();
     });});

ToTable 的调用在此处是必须的,否则按默认约定,它会使用表名 Laps,即和 Laptop 保持一致,这会导致出错。而且,Laptop 和 Desktop 不能共享一个 HardwareInfo 实体。这样配置也会报错:

protected override void OnModelCreating(ModelBuilder mb)
{mb.Entity<Desktop>(et =>{et.HasKey(a => a.ID);et.OwnsOne(b => b.HWInfo, ob =>{ob.ToTable("HW_info");});});mb.Entity<Laptop>(et =>{et.HasKey(a => a.ID);et.OwnsOne(m => m.HWInfo, ob =>{ob.ToTable("HW_info");});});
}

这就等于 Desktop 和 Laptop 同时占有相同的 HardwareInfo 实例,运行时也会报错。

 

One and Many

 这里咱们已经没有必要再与普通的一对多关系对比了,上面的对比已经明确 Owned 关系是独占性的,不共享实例。下面咱们看看实体独占多个实例的情况。这种情况下,被占有的对象不会与主对象共用一个表了——拆分的列无法表示多个实例。

举个例子。

public class AddressInfo
{/// <summary>/// 这里有主键/// </summary>public int AddrID {  get; set; }/// <summary>////// </summary>public string Province { get; set; } = "";/// <summary>////// </summary>public string City { get; set; } = "";/// <summary>////// </summary>public string Town { get; set; } = "";/// <summary>////// </summary>public string Road { get; set; } = "";/// <summary>/// 街道/// </summary>public string Street { get; set; } = "";/// <summary>/// 邮编/// </summary>public string? ZipCode { get; set; }
}public class Student
{public int StudentID { get; set; }public IList<AddressInfo>? Addresses { get; set; }
}

如果这里的地址表示收货地址,于是每个学生都可以拥有多个地址。

然后,上下文类是这样的。

public class MyContext : DbContext
{public DbSet<Student> Students { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder ob){SqlConnectionStringBuilder strbd = new();strbd.DataSource = <你的服务器>;strbd.InitialCatalog = "TestDB";ob.UseSqlServer(strbd.ConnectionString).LogTo(x => Console.WriteLine(x));}protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.Entity<Student>(ste =>{ste.HasKey(x => x.StudentID).HasName("PK_Stu_id");// 它占有多个 Addrste.OwnsMany(k => k.Addresses, ob =>{// 此处可以配置主键ob.HasKey(x => x.AddrID);ob.WithOwner().HasForeignKey("stu_id").HasConstraintName("FK_StuID");});});}
}

数据库会创建两张表:

CREATE TABLE [Students] ([StudentID] int NOT NULL IDENTITY,CONSTRAINT [PK_Stu_id] PRIMARY KEY ([StudentID]));CREATE TABLE [AddressInfo] ([AddrID] int NOT NULL IDENTITY,[Province] nvarchar(max) NOT NULL,[City] nvarchar(max) NOT NULL,[Town] nvarchar(max) NOT NULL,[Road] nvarchar(max) NOT NULL,[Street] nvarchar(max) NOT NULL,[ZipCode] nvarchar(max) NULL,[stu_id] int NOT NULL,CONSTRAINT [PK_AddressInfo] PRIMARY KEY ([AddrID]),CONSTRAINT [FK_StuID] FOREIGN KEY ([stu_id]) REFERENCES [Students] ([StudentID]) ON DELETE CASCADE);

AddressInfo 表会创建一个外键来引用 Students 表的主键列。

接着,咱们加一个 Teacher 实体,和学生一样,老师也有多个收货地址。

public class Teacher
{public int Tid { get; set; }public IList<AddressInfo>? Addresses { get; set; }
}

上下文类也要做相应修改。

public class MyContext : DbContext
{public DbSet<Student> Students { get; set; }public DbSet<Teacher> Teachers { get; set; }……protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.Entity<Student>(ste =>{ste.HasKey(x => x.StudentID).HasName("PK_Stu_id");// 它占有多个 Addrste.OwnsMany(k => k.Addresses, ob =>{// 此处可以配置主键ob.HasKey(x => x.AddrID);// 必须要表名ob.ToTable("Stu_Addr");ob.WithOwner().HasForeignKey("stu_id").HasConstraintName("FK_StuID");});});modelBuilder.Entity<Teacher>(tet =>{tet.HasKey(t => t.Tid).HasName("PK_TeacherID");// 占用多个地址tet.OwnsMany(t => t.Addresses, ob =>{ob.HasKey(o => o.AddrID);   // 主键ob.ToTable("Teacher_Addr"); // 表名ob.WithOwner().HasForeignKey("teach_id").HasConstraintName("FK_TeachID");});});}
}

这种情况下必须配置 AddressInfo 的表名。

这样数据库会创建四张表:

CREATE TABLE [Students] ([StudentID] int NOT NULL IDENTITY,CONSTRAINT [PK_Stu_id] PRIMARY KEY ([StudentID]));CREATE TABLE [Teachers] ([Tid] int NOT NULL IDENTITY,CONSTRAINT [PK_TeacherID] PRIMARY KEY ([Tid]));CREATE TABLE [Stu_Addr] ([AddrID] int NOT NULL IDENTITY,[Province] nvarchar(max) NOT NULL,[City] nvarchar(max) NOT NULL,[Town] nvarchar(max) NOT NULL,[Road] nvarchar(max) NOT NULL,[Street] nvarchar(max) NOT NULL,[ZipCode] nvarchar(max) NULL,[stu_id] int NOT NULL,CONSTRAINT [PK_Stu_Addr] PRIMARY KEY ([AddrID]),CONSTRAINT [FK_StuID] FOREIGN KEY ([stu_id]) REFERENCES [Students] ([StudentID]) ON DELETE CASCADE);CREATE TABLE [Teacher_Addr] ([AddrID] int NOT NULL IDENTITY,[Province] nvarchar(max) NOT NULL,[City] nvarchar(max) NOT NULL,[Town] nvarchar(max) NOT NULL,[Road] nvarchar(max) NOT NULL,[Street] nvarchar(max) NOT NULL,[ZipCode] nvarchar(max) NULL,[teach_id] int NOT NULL,CONSTRAINT [PK_Teacher_Addr] PRIMARY KEY ([AddrID]),CONSTRAINT [FK_TeachID] FOREIGN KEY ([teach_id]) REFERENCES [Teachers] ([Tid]) ON DELETE CASCADE);

 

最后,咱们验证一下,Owned 关系是否真的不能共享实例。

using(MyContext c = new())
{// 四个地址AddressInfo addr1 = new(){Province = "冬瓜省",City = "嘎子市",Town = "小连子镇",Road = "牛逼路",Street = "春风街3999号",ZipCode = "62347"};AddressInfo addr2 = new(){Province = "提头省",City = "抬扛台",Town = "烟斗镇",Road = "王八路",Street = "送人头街666号",ZipCode = "833433"};// 教师实例Teacher tt = new();// 学生实例Student ss = new();// 让他们使用相同的地址实例tt.Addresses = new List<AddressInfo>( [addr1, addr2] );ss.Addresses = new List<AddressInfo>( [addr1, addr2] );// 添加实体
    c.Students.Add(ss);c.Teachers.Add(tt);// 保存到数据库
    c.SaveChanges();
}

运行后,未抛出异常,但有警告。而且数据库中也有数据。

下面咱们改一下某个地址的 City 属性。

using(MyContext c2 = new())
{var r1 = c2.Students.ToArray();var r2 = c2.Teachers.ToArray();AddressInfo? addr = r1.First()?.Addresses?.FirstOrDefault();if(addr != null){addr.City = "烤鸭市";}c2.SaveChanges();
}

运行一下。

然后咱们查询一下两个地址表的数据。

select * from Stu_Addr;
select * from Teacher_Addr;

image

只有 ID = 1 的学生的第一个地址的 City 属性被更新,而教师地址未更新。可见,两个实体是不共响地址实例的。这很好理解嘛,毕竟是两个表的。

 

那么,如果把 Student - AddressInfo,Teacher - AddressInfo 的关系改为普通的一对多关系,又会怎样?

public class MyContext : DbContext
{public DbSet<Student> Students { get; set; }public DbSet<Teacher> Teachers { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder ob){……}protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.Entity<Student>(ste =>{ste.HasKey(x => x.StudentID).HasName("PK_Stu_id");ste.HasMany(x => x.Addresses).WithOne().HasForeignKey("stu_id").HasConstraintName("FK_StuID");});modelBuilder.Entity<Teacher>(tet =>{tet.HasKey(t => t.Tid).HasName("PK_TeacherID");tet.HasMany(f => f.Addresses).WithOne().HasForeignKey("teacher_id").HasConstraintName("FK_TeacherID");});// 注意:这时候 AddressInfo 实体需要主键modelBuilder.Entity<AddressInfo>().HasKey(x => x.AddrID);}
}

改为普通一对多关系时要注意,Student、Teacher、AddressInfo 三个实体都需要主键的, Owned 实体、复合类型(老周以前介绍过)这些不需要主键。

删除刚刚的数据库,重新建立新的数据库,然后写入数据。

using(MyContext c = new())
{c.Database.EnsureDeleted();c.Database.EnsureCreated();// 两个地址AddressInfo addr1 = new(){Province = "冬瓜省",City = "嘎子市",Town = "小连子镇",Road = "牛逼路",Street = "春风街3999号",ZipCode = "62347"};AddressInfo addr2 = new(){Province = "提头省",City = "抬扛台",Town = "烟斗镇",Road = "王八路",Street = "送人头街666号",ZipCode = "833433"};// 教师实例Teacher tt = new();// 学生实例Student ss = new();// 让他们使用相同的地址实例tt.Addresses = new List<AddressInfo>( [addr1, addr2] );ss.Addresses = new List<AddressInfo>( [addr1, addr2] );// 添加实体
    c.Students.Add(ss);c.Teachers.Add(tt);// 保存到数据库
    c.SaveChanges();
}

这时候,地址表只有一个,插入的数据如下:

image

教师和学生共享一个地址表,分别通过 stu_id 和 teacher_id 外键引用主表记录。

然后更改第一个地址的 City 属性。

 using(MyContext c2 = new()){var r1 = c2.Students.Include(s => s.Addresses).ToArray();var r2 = c2.Teachers.Include(t => t.Addresses).ToArray();AddressInfo? addr = r1.First()?.Addresses?.FirstOrDefault();if(addr != null){addr.City = "烤鸭市";}c2.SaveChanges();}

地址表的数据变为:

image

由于教师和学生共用一个地址表,所以他们的地址信息会相同。

 using(MyContext c3 = new()){// 加载全部数据var students = c3.Students.Include(x => x.Addresses);var teachers = c3.Teachers.Include(x => x.Addresses);Console.WriteLine("---------- 学生 ---------");foreach(var s in students){Console.WriteLine($"学生:{s.StudentID}");if(s.Addresses != null){foreach(var a in s.Addresses){Console.WriteLine($"\t{a.AddrID}, {a.Province}, {a.City}, {a.Town}");}}}Console.WriteLine("\n---------- 教师 ---------");foreach (var t in teachers){Console.WriteLine($"老师:{t.Tid}");if (t.Addresses != null){foreach (var a in t.Addresses){Console.WriteLine($"\t{a.AddrID}, {a.Province}, {a.City}, {a.Town}");}}}}

image

 

【总结】

1、Owned 关系中,主实体完全掌控从实体,并且不与其他实体共享数据;

2、被“独占”的实体不用使用 ModelBuilder.Entity<T> 方法配置,因此在 DbContext 派生时,也不能声明为 DbSet<T> 属性。而普通关系中的实体是允许的;

3、Owned 关系有一 Own 一、一 Own 多,不存在 多 Own 多。多 Own 多 就违背“独占”原则了。普通关系中可以有多对多;

 

相关文章:

【EF Core】再谈普通实体关系与 Owned 关系的区别

在很多个世纪前,老周曾写过实体之间普通关系(一对一,一对多,多对多)与 Owned 关系的区别。不过,那次写得比较粗浅,逼格不够高,于是,老周厚着脸皮地决定重新写一下。 首先,为什么这次老周用原单词 Owned 呢,官方文档目前的翻译(怀疑是机器干的)为“从属”,这种说法…...

qoj6104 Building Bombing

题意 有 \(n\) 栋建筑,第 \(i\) 栋建筑的高度为 \(a_i\),一座建筑能从左侧看到仅当它左侧的建筑高度都小于它,问你最少需要爆破几座房子,才能使第 \(l\) 座房子成为能看到的第 \(k\) 高建筑。 \(n\le 10^5,k\le 10\)。 思路 首先 \(l\) 要能被看到,因此先把 \(l\) 左边高度…...

必知必会:使用serializers.Serializer在views.py视图文件中序列化和反序列化过程的开发模板

from django.views import Viewclass ProjectTestView(View):"""a.获取所有数据b.创建一条数据"""def get(self, request):"""序列化输出到前端的过程规则:a.创建模型对象b.将上面已创建好的的模型对象传递给序列化器类的instan…...

Cursor小程序实战五:Cursor对接微信两大核心问题

一、对话内容非技术人员的语言 程序员/技术人员的语言比如:回调地址,api,发送请求 ..... 二、微信的两大问题:授权、微信支付 目的是对于非技术人员能够输出一段比较精确的提示词,方便完成功能的开发 1)微信授权登陆 1、永远绕不开用户登陆流程用户登录 手机号验证码…...

电商系统的Mysql表设计是怎么样呢

一、前述 问题1: 电商系统创建订单的逻辑中,如果订单使用了优惠券的话,是会把优惠券直接标在订单表么,还是会单独创建一个表里记录订单和多个优惠券之间的关系 问题2: 如何设计一套mysql数据库的表,可以将订单信息、支付信息、优惠券信息以及商品信息之间关联起来二、实战演练…...

Docker应用 - CloudSaver

CloudSaver 是一个网盘搜索、转存工具。首次部署需要先注册用户,默认管理员注册码 230713。注册进入后可以在设置里修改管理员和用户注册码。用户配置处,可以登录多种网盘授权(可惜现在不支持百度了)。不设置也可以搜索,但不能直接转存。在常见问题可以查找搜索频道(密码…...

SQL查找是否存在,别再count了! - DAYTOY

根据某一条件从数据库表中查询 『有』与『没有』,只有两种状态,那为什么在写SQL的时候,还要SELECT count(*) 呢?无论是刚入道的程序员新星,还是精湛沙场多年的程序员老白,都是一如既往的count 1 目前多数人的写法 多次REVIEW代码时,发现如现现象:业务代码中,需要根据一…...

Cursor小程序实战系列二:如何从原型界面到小程序界面

一、原型界面的生成 在cursor中用以下提示词我想开发一个小程序 目标用户: • 主要用户:25-45岁的家长,没时间来教小孩怎么写作文,也可能不知道还在对应年级作文的写作规范和要求 • 用户痛点:1、家长难以针对孩子写作弱点提供定制化提升方案2、无法获得实时写作建议 我目…...

Cursor小程序实战系列三: 前后端对接保姆级拆解

一、先理解下什么是前后端,为什么叫对接? 二、谁来定义API接口 1、传统的模式 先后端出接口定义,前端人员按照接口定义自己搭建模拟数据,这样的好处是 前端,后端都能同时开发,互不影响 2、cursor模式 三、生成API接口文档 1、最好的方式,在rules中定义接口变化同时更新…...

课前问题思考2

1.方法相关问题 public class Main {static void changeStr(String x) {x = "xyz";}static void changeArr(String[] strs) {for (int i = 0; i < strs.length; i++) {strs[i] = strs[i]+""+i;}}public static void main(String[] args) { String x …...

Cursor小程序实战四:如何让AI写好后端代码

五、用好mermaid流程画图工具...

Web 3

Web 3 更改前题目 题目没有做记录,卡在了最后一步正则绕过,前七个绕过挺简单的 ?A[]=1&B[]=2&C=s878926199a&D[]=0&E=5201.1&F=0xDEADC0DE正则绕过: if (!preg_match(/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zi…...

Cursor小程序实战系列一:0到1开发一个小程序,需求整理、小程序注册备案

一、需求的诞生 某书发表了一片笔记: 家长使用多维表格+DeepSeek对小孩进行作文辅导。痛点两到三个核心功能点这些内容对使用Cursor来生成页面很有帮助AI作文辅导一般的家长辅导孩子作文非常困难,主要原因如下:1、家长难以针对孩子写作弱点提供定制化提升方案 2、无法获得实时…...

深入解析:MySQL 数据类型与运算符详解

深入解析:MySQL 数据类型与运算符详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !importan…...

【前端Vue】如何优雅地在vue中引入ace-editor编辑器 - 指南

【前端Vue】如何优雅地在vue中引入ace-editor编辑器 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …...

USACO08 OPEN Roads Around the Farm S (递归)

(我太垃了,得写点题解提升一下) P2907 [USACO08OPEN] Roads Around The Farm S 题目描述 Farmer John 的奶牛对探索农场周围的领地产生了兴趣。最初,所有 $N$ 头奶牛($1 \leq N \leq 10^9$)以一个大群体的形式开始沿着一条道路旅行。当遇到岔路时,群体有时会选择分成两个…...

dubbo-go example学习记录

dubbo-go example仓库地址: https://github.com/apache/dubbo-go-samples你可以在 https://github.com/apache/dubbo-go-samples/blob/main/README_CN.md 查看中文的README文档快速上手 这个是通过 https://cn.dubbo.apache.org/zh-cn/blog/2021/01/14/快速上手-dubbo-go/ 官方…...

org.apache.commons.lang3.StringUtils工具类中 isEmpty 和 isBlank 的区别 - DAYTOY

1 isEmpty系列 1.1 StringUtils.isEmpty() 说明:是否为空. 可以看到 " " 空格是会绕过这种空判断,因为是一个空格,并不是严格的空值,会导致 isEmpty(" ")=falseStringUtils.isEmpty(null) = true StringUtils.isEmpty("") = true StringUtils.i…...

ubuntu如何查看是否有显卡,显卡型号

在 Ubuntu 系统中,查看是否有显卡以及显卡型号的方法有多种。以下是几种实用的命令,方便你快速获取显卡信息。1. 使用 lspci 查看显卡信息 lspci 命令会列出所有 PCI 设备,包括显卡。 命令bashlspci | grep -i vga输出示例 00:02.0 VGA compatible controller: Intel Corpor…...

赛题

A U607526 「Monkey Mountine Round I」乔迁新居 题目背景 天大的喜事,游荡几十载之后,猴王找到了水帘洞! 但是,洞口较小,不知猴族老小和辎重几次能运完。善武不通文的猴王一代广招贤才,找你算算。 题目描述 共有 \(n\) 只猴子,\(m\) 车辎重。洞口每次可以进入 \(x\) 只…...

JavaScript生成随机数的方法

在JavaScript中,创建随机数可以通过内置的 Math 对象实现。我们将会探讨一些生成随机数的方法,它们在不同场合下都显示出色。 基本的随机数生成 // 生成0到1之间的随机数(不包含1) let randomNumber = Math.random(); Math.random() 函数返回一个浮点数,该数值在0(包括0)…...

LiveOS 的制作简介

LiveOS 用途:便捷启动 LIVEOS 不占用磁盘空间,系统完全在内存文件系统中运行 常用于 linux 系统救援(文件系统异常导致系统进入紧急模式) 常用于制作 PXE 生产测试 DIAG 系统 开源,免费,可定制化程度高制作工具 lorax lorax 项目是 红帽企业版 OS 的安装器的开源项目 套件包…...

.gitignore 文件

...

目标检测 | 基于Weiler–Atherton算法的IoU求解

**交并比(Intersection over Union, IoU)** 是计算机视觉领域中常用的一个评价指标,尤其在目标检测与图像分割任务中,用于衡量预测结果与真实标注之间的重合程度。目标检测 | 基于Weiler–Atherton算法的IoU求解 IoU 交并比(Intersection over Union, IoU) 是计算机视觉领…...

对比Java学习Go——函数、集合和OOP

Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了…...

MySQL集群高可用架构 - 指南

MySQL集群高可用架构 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-si…...

【WRF-VPRM 预处理器】HEG 安装(服务器)-MRT专业的工具替代

【WRF-VPRM 预处理器】HEG 安装(服务器)-MRT专业的工具替代pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New"…...

如何在Spring MVC中处理请求参数

在Spring MVC中处理请求参数是通过使用各种注解来实现的。以下是在Spring MVC中处理不同类型请求参数的方法。 使用 @RequestParam注解 当你想要从查询字符串中获取单个参数值时,你可以使用 @RequestParam注解。例如: @GetMapping("/search") public String search…...

redis实现缓存2-解决缓存穿透,缓存击穿

具体实现: ShopServiceImpl package com.hmdp.service.impl;import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.hmdp.dto.Result; import com.hmdp.entity.Shop; …...

单克隆抗体人源化:从鼠源缺陷到全人源突破,3 大阶段破解临床应用难题

单克隆抗体(McAb)凭借高特异性、强靶向性,在疾病预防、诊断与治疗中占据核心地位。1975 年,Khler 和 Milstein 创立杂交瘤技术,首次实现人工制备 McAb,为生物医药领域开辟新路径。但初代鼠源性 McAb 存在两大关键缺陷,严重限制临床应用:一是免疫原性高,进入人体后易被…...

在Kubernetes中DaemonSet无法在master节点调度的问题

在Kubernetes中,DaemonSet确保全部(或某些特定)Node运行一个Pod的副本。当有Node加入集群时,DaemonSet会自动在新加入的Node上部署Pod。这对于运行像日志收集器、监控代理或其他形式的守护进程非常有用。 默认情况下,出于安全性的考虑,Kubernetes master节点不允许调度普…...

9 12-

9 12改一道题改了一天,自闭了,总结无法描述,还是自己太糖了/ll P8776 线段树优化DP转移9 13模拟赛唯一一场没有睡着的模拟赛 T1很快想到了换根DP,秒掉 T2很快想到了 \(N^2\) 的暴力,然后经过我的观察发现转移形似杨辉三角,就推了出来 T3T4毫无思路剩下一个半小时直接跑路…...

桌面客户端的主要类型和技术方案

桌面客户端开发已经不再是传统的单一技术栈,而是衍生出了多种方案,各有优劣。下图清晰地展示了这些技术方案的演进与分类: flowchart TD A[桌面客户端技术方案] --> B1["原生开发<br>(Native App)"] A --> B2["跨平台开发<br>(Cross-Platf…...

AGX Orin平台RTC驱动导致reboot系统卡住障碍调试

AGX Orin平台RTC驱动导致reboot系统卡住障碍调试pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace …...

C 语言实现动态数组、链表、栈与队列

从代码到原理:C语言实现动态数组、链表、栈与队列 在数据结构的世界里,线性结构是构建复杂算法的基石。动态数组、链表、栈和队列作为最经典的线性结构,各自拥有独特的存储方式与操作特性,适用于不同的业务场景。本文将结合C语言实现代码,从结构定义、核心操作到实际应用,…...

git reset

在一个文件夹内,初始化其为 git 本地仓库,然后新建一个文件,提交至本地仓库,再修改这个文件,再提交至本地仓库。此时此刻的提交记录:如果用 git reset 命令回到当前所在位置,是不会有任何变化的。用 git reset 命令回到位于当前提交之前的提交,这一步操作也可以复原:如…...

ICPC 2025 网络赛第一场 M

这道题我本来是建立多层图然后跑dijkstra来解决,但是由于N=5000,所以会包空间导致RE或者MLE,注意到其实这道题是从1到n都来一遍,其实就可以考虑k-1和k的关系,k在k- 1的基础上面跑最短路,跑完了之后我们对比传送门的两个点到1的距离,这样它可以更新新的最短路。 #include…...

Brute It -TryHackMe

Brute It -TryHackMe 一、信息收集使用nmap对网站ip开放端口进行搜集使用dirsearch发现网站下面有个admin目录访问看看是一个管理员登录界面在这个页面右键源代码发现了给我们的提示,告诉我们这个网站的账户是admin,我们抓包使用yakit进行爆破二、枚举爆破接下来使用hydra对网…...

题解:P12336 第三心脏

题目链接。作者没看过第三心脏,所以作者猜测第三个心脏应该是用铁做的,由于铁粉是黑的,所以这道题目是黑。养成良好习惯,不留根号,式子变为: \[a^2+b^2+c^2+d^2=\left(a\oplus b\oplus c\oplus d\right)^2 \]注意到 \(a\ge 1\) 所以 \(a^2+b^2+c^2+d^2>d^2\) 又注意到…...

Spring篇知识点(1)

一、Spring框架的特性 IOC和DI支持:Spring 的核⼼就是⼀个⼤的⼯⼚容器,可以维护所有对象的创建和依赖关系,Spring ⼯⼚⽤于⽣成Bean,并且管理 Bean 的⽣命周期,实现⾼内聚低耦合的设计理念。 AOP编程支持:方便实现对程序进行权限拦截、运行监控等切面功能 声明式事务支持…...

在CentOS 7系统中彻底移除MongoDB数据库

彻底移除CentOS 7系统中的MongoDB数据库,需要进行以下步骤:停止MongoDB服务:首先确保MongoDB服务已经停止,可以通过下面的命令来执行这一操作:sudo systemctl stop mongod 如果您的MongoDB服务名称不是默认的 mongod,请将上述命令中的 mongod替换为实际的服务名称。删除M…...

【数学建模】烟幕干扰弹投放策略优化:模型与算法整合框架 - 实践

【数学建模】烟幕干扰弹投放策略优化:模型与算法整合框架 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New&q…...

2025.9.13总结

T1 神秘结论,因为轮数是可以算出来的,然后依次把次数取 min 然后堆起来就是对的。可以 \(O(n)\) 做完。 T2 一次修改的作用是明显的。 答案最大为 2,因为可以 max->2highbit->2(highbit+1)-1。 考虑答案为 1,那么就是跨过所有 0。但是因为覆盖后后面和前面的都没了,…...

开源排名算法工具raink:利用LLM实现智能文档排序

本文介绍Bishop Fox开源的raink工具,该工具采用基于大语言模型的列表排序算法,能够解决复杂排名问题,包括将代码差异与安全公告关联,并详细说明其算法原理及在漏洞识别中的应用场景。raink:使用LLM进行文档排序 TL;DR:Bishop Fox发布了raink,这是一个使用新型基于LLM的列…...

lcjmSSL域名SSL证书免费申请

想为您的网站轻松开启HTTPS安全加密吗?lcjmSSL(来此加密)为您提供完全免费的SSL证书服务!无论是单个站点、多个域名还是需要守护整个子站群的泛域名证书,我们都能满足。单证书最高支持100个域名的极致灵活性,助您以零成本构建更安全、更可信的网站环境。立即体验,为您的…...

uniapp原生插件 TCP Socket 利用文档

uniapp原生插件 TCP Socket 利用文档pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important;…...

【PyQt5】实现输入延迟响应:3秒无输入后自动读取内容

思路:每次输入框内容改变,都重置 QTimer 倒计时为 3 秒;当持续 3 秒无输入后,QTimer 超时,获取当前输入框内容。UI 代码(untitled.py):点击查看代码 from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Form(object):def setupUi(self, Form):Form.setObjectName(&qu…...

线性代数基础

暂无...

微积分基础

暂无...

Windows 自带的SSH中配置X11

本文介绍了给Windows 11中自带的SSH配置X11的方法1.安装 Windows的Xserver很多如:Xming 和 VcXsrv。Xming和VcXsrv都是X服务器软件,允许在Windows系统上运行Linux图形界面应用程序。它们的关系可以从以下方面概括:历史渊源 Xming最初由Colin Harrison于2004年开发,基于X.Or…...