EntityFramework Core问题处理集锦(一)

简介:

前言

和大家脱离了一段时间,有时候总想着时间挤挤总是会有的,但是并非人愿,后面会借助周末的时间来打理博客,如有问题可以在周末私信我或者加我QQ皆可,欢迎和大家一起探讨,本节我们来讨论EF Core中的一些问题后面陆陆续续会将EF Core中需要注意的地方补充上来,有些是我一直以来比较疏忽的地方,不喜勿喷。用在实际项目中的时候才发现和平时所学有很大差异,靠着项目才能检验出真理。

EntityFramework Core问题集锦

更新单个实体

更新单个实体的方式有两种:

(1)查询出实体进行赋值更新

说的更专业一点则是已被跟踪的实体进行赋值更新,此时实体已被快照,此时进行更新时只需要调用SaveChanges或者SaveChangesAsync,当已赋值属性与快照中值不同时,此时调用SaveChangesAsync或者SaveChanges方法时会将此属性的状态即(IsModified)修改为True,否则为False。代码大概如下:

复制代码
        public async Task<bool> UpdateStatus(int id, byte status)
        {
            var blog = _efCoreContext.Blogs.Find(id);
            blog.Status = status;
            var effectRows = await _efCoreContext.SaveChangesAsync(CancellationToken.None);
            if (effectRows > 0)
            {
                return true;
            }
            return false;
        }
复制代码

但是如上又带来一个问题,我们通过影响行数来获取是否更新成功,如果想更新某一列,但是此列的值未进行改变,此时与快照中的值一致,则受影响行数为0,结果返回的更新失败,如下所示:

在这种情况就需要一个扩展方法来显式指定更新属性即使值未发生改变也将其属性状态IsModified修改为True,这样才不会导致值未改变但是更新失败的情况,即如下:

 _efCoreContext.Entry(blog).Property(d => d.Status).IsModified = true;

(2)未查询出实体进行赋值更新。

当此实体未进行查询,此时需要调用Update来将此实体状态中所有属性的IsModified修改为True,此时代码大概如下:

复制代码
        public async Task<bool> UpdateStatus(Blog blog)
        {
            _efCoreContext.Blogs.Update(blog);
            var effectRows = await _efCoreContext.SaveChangesAsync(CancellationToken.None);
            if (effectRows > 0)
            {
                return true;
            }
            return false;
        }
复制代码

也就是说如果是明确实体所有属性都会更改则可以利用Update方法来更新所有属性,否则不需要更新的属性比如常见场景:数据库中表中数据创建时间下次进行更新时是不需要更新,如若调用Update方法,如果对创建时间赋值会进行覆盖,未赋值则会显示DateTime默认时间。

批量更新之表达式树

批量更新的场景大有,在我们项目中选择多个产品将产品的状态更新为下架状态,下面我们来还原场景。创建批量更新接口,此时数据库中数据如下:

Task<bool> UpdateStatus(int[] ids);

我们将Blog中状态中为0的行更新为1,此时接口则如下:

复制代码
        public async Task<bool> UpdateStatus(int[] ids)
        {
            var blogs = _efCoreContext.Blogs.Where(d => ids.Contains(d.Id));

            blogs.Select(b => new Blog() { Id = b.Id, Status = 1 }).ToList();

            if (await _efCoreContext.SaveChangesAsync(CancellationToken.None) > 0)
            {
                return true;
            }
            return false;
        }
复制代码

此时更新肯定不能正确更新,其原因不必多讲,由于是更新集合中的指定属性,此时我写了关于单个和集合更新指定属性的扩展方法,如下:

复制代码
    public static class EfCoreUpdateExe
    {
        public static void Update<T>(this EFCoreContext context, T entity, params Expression<Func<T, object>>[] properties) where T : class, new()
        {
            var dbEntityEntry = context.Entry(entity);
            if (properties.Any())
            {
                foreach (var property in properties)
                {
                    dbEntityEntry.Property(property).IsModified = true;
                }
            }
            else
            {
                foreach (var rawProperty in dbEntityEntry.Entity.GetType().GetTypeInfo().DeclaredProperties)
                {
                    var originalValue = dbEntityEntry.Property(rawProperty.Name).OriginalValue;
                    var currentValue = dbEntityEntry.Property(rawProperty.Name).CurrentValue;
                    foreach (var property in properties)
                    {
                        if (originalValue != null && !originalValue.Equals(currentValue))
                            dbEntityEntry.Property(property).IsModified = true;
                    }

                }
            }
        }



        public static void UpdateRange<TEntity>(this EFCoreContext context, IEnumerable<TEntity> entities, bool isNoTracking = true, params Expression<Func<TEntity, object>>[] properties) where TEntity : class, new()
        {
            foreach (var entity in entities)
            {
                var dbEntityEntry = context.Entry(entity);
                //Notice that:当更新实体指定属性时,若实体从数据库中查询而出,此时实体已被跟踪,则无需处理,若实例化对象而更新对象指定属性,此时需要将其状态修改为Unchanged即需要附加
                if (!isNoTracking) { dbEntityEntry.State = EntityState.Unchanged; }
                if (properties.Any())
                {
                    foreach (var property in properties)
                    {
                        dbEntityEntry.Property(property).IsModified = true;
                    }
                }
                else
                {
                    foreach (var rawProperty in dbEntityEntry.Entity.GetType().GetTypeInfo().DeclaredProperties)
                    {
                        var originalValue = dbEntityEntry.Property(rawProperty.Name).OriginalValue;
                        var currentValue = dbEntityEntry.Property(rawProperty.Name).CurrentValue;
                        foreach (var property in properties)
                        {
                            if (originalValue != null && !originalValue.Equals(currentValue))
                                dbEntityEntry.Property(property).IsModified = true;
                        }

                    }
                }
            }
        }
    }
复制代码

然后代码更新代码修改如下:

复制代码
        public async Task<bool> UpdateStatus(int[] ids)
        {
            var blogs = _efCoreContext.Blogs
                .Where(d => ids.Contains(d.Id));

            var updateProductList = blogs.Select(b => new Blog() { Id = b.Id, Status = 1 }).ToList();

            _efCoreContext.UpdateRange(updateProductList, true, d => d.Status);
            
            if (await _efCoreContext.SaveChangesAsync(CancellationToken.None) > 0)
            {
                return true;
            }
            return false;
        }
复制代码

此时与数据库会进行两次连接,一次是查询,一次是更新指定属性字段,通过SQL跟踪我们能看到如下语句:

SELECT [d].[Id]
FROM [Blog] AS [d]
WHERE [d].[Id] IN (2, 3, 5, 6)
复制代码
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Blog] SET [Status] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

UPDATE [Blog] SET [Status] = @p2
WHERE [Id] = @p3;
SELECT @@ROWCOUNT;

UPDATE [Blog] SET [Status] = @p4
WHERE [Id] = @p5;
SELECT @@ROWCOUNT;

UPDATE [Blog] SET [Status] = @p6
WHERE [Id] = @p7;
SELECT @@ROWCOUNT;

',N'@p1 int,@p0 tinyint,@p3 int,@p2 tinyint,@p5 int,@p4 tinyint,@p7 int,@p6 tinyint',@p1=2,@p0=1,@p3=3,@p2=1,@p5=5,@p4=1,@p7=6,@p6=1
复制代码

最终正确更新如下:

除了上述通过写反射扩展方法来更新外属性外,一直在想着其中会进行两次数据库链接,进行一次数据库链接比较耗时,这个时候想到的只能执行SQL命令了。

批量更新之SQL命令

利用WHERE ....IN来进行更新,此时SQL更新代码则如下:

复制代码
        public async Task<bool> UpdateStatus(int[] ids)
        {
            var testIds = string.Join(",", ids);

            var effctRow = await _efCoreContext.Database.ExecuteSqlCommandAsync("update dbo.Blog set [Status] = 1 where id in ({0})", CancellationToken.None, testIds);
            if (effctRow > 0)
            {
                return true;
            }
            return false;
        }
复制代码

不知道各位看客发现什么没有,上述的代码是有问题的,哪里有问题,不知道的请看如下动态演示。

 

正常情况下WHERE...IN(2,3,5,6)而非上述“2,3,5,6”,此时我将上述代码修改为如下:

复制代码
        public async Task<bool> UpdateStatus(int[] ids)
        {
            var blogIds = string.Empty;
            foreach (var id in ids)
            {
                blogIds += $"{id},";
            }
            blogIds = blogIds.TrimEnd(',');
            var effectRows = await _efCoreContext.Database.ExecuteSqlCommandAsync("update dbo.Blog set [Status] = 1 where id in ({0})", CancellationToken.None, blogIds);
            if (effectRows > 0)
            {
                return true;
            }
            return false;
        }
复制代码

此时再来看看演示效果:

此时则报NVARCHAR转换到INT失败,那么粗暴一点将id转换为NVARCHAR:

 var effectRows = await _efCoreContext.Database.ExecuteSqlCommandAsync("update dbo.Blog set [Status] = 1 where cast(id as nvarchar(max)) in ({0})", CancellationToken.None, blogIds);

当然如上涉及到索引,通过函数转换不会走索引,我们正常情况下应该是定义一个变量将id进行转换,然后利用变量来进行包含。此时再来看演示效果:

此时压根都没去更新,我也是醉了,最后我们再来看一种情况,我们写SQL命令通过拼接的形式来进行,如下:

 var effectRows = await _efCoreContext.Database.ExecuteSqlCommandAsync($"update dbo.Blog set [Status] = 1 where id in ({blogIds})", CancellationToken.None);

此时居然更新成功了,其实我们利用上述字符串拼接的方式进行如下两种转换都会更新成功:

复制代码
             //转换方式一
            //var blogIds = string.Join(",", ids);

            //转换方式二
            var blogIds = string.Empty;
            foreach (var id in ids)
            {
                blogIds += $"{id},";
            }
            blogIds = blogIds.TrimEnd(',');
            var effectRows = await _efCoreContext.Database.ExecuteSqlCommandAsync($"update dbo.Blog set [Status] = 1 where id in ({blogIds})", CancellationToken.None);
复制代码

但是利用$符号本质无非是简化了string.format的书写罢了,容易导致SQL注入的问题,但是利用参数化SQL对于WHERE....IN情况就是无法进行更新,对于删除亦是如此,上述未曾演示利用SqlParameter来进行更新,如果你这样做了,结果依然一样不好使:

复制代码
            var blogIds = string.Empty;
            foreach (var id in ids)
            {
                blogIds += $"{id},";
            }
            blogIds = blogIds.TrimEnd(',');
            var parameters = new SqlParameter[]
            {
                new SqlParameter("@ids",System.Data.SqlDbType.NVarChar,400){ Value = blogIds }
            };
            var effectRows = await _efCoreContext.Database.ExecuteSqlCommandAsync("update dbo.Blog set [Status] = 1 where id in (@ids)", CancellationToken.None, parameters);
复制代码

上述是对于更新的主键为INT的情况,若是主键为字符串,此时这种情况更加突出,因为对于字符串形式需要这样的格式IN('A','B','C'),此时我们将上述id看作为字符串,我们进行如下转换:

var blogIds = "'" + string.Join("','", ids) + "'";

然后去进行更新,参数正确,格式也正确,但是就是无法进行更新。最终统一得出的结论是:

进行批量更新或者删除的情况利用WHERE....IN参数化SQL无法进行更新或者删除,利用$或者string.format进行拼接却好使,但是会导致SQL注入。

上述演示EF Core版本为1.1.2,遇到这样的问题是在进行批量删除时,有人反问了批量删除不是有RemoveRange么,但是其中涉及到多表查询然后进行批量删除,就是期望达到一步到位的效果,最终没有办法,我采用LINQ的方法利用两步来进行批量删除,看到此文的你对于EF Core中利用SQL(WHERE....IN)命令来进行批量删除或者更新的情况见解是怎样,是否有遇到这样的问题,如果利用参数化SQL解决了问题的话望告知。

2017-08-07利用WHERE...IN参数化SQL批量更新或删除

这两天人感冒,什么都不想干,回来太早又没事干,于是乎再次回顾了下这个问题,我天真的以为在ADO.NET中利用WHERE...IN用SQL的方式来进行批量更新呢或者删除是好使的,结果一试居然一样不好使,有人想了为何不利用存储过程解决何必纠结于此,想了想就一句话的事情,没必要还搞个存储过程而且还要打开数据库操作(我懒)。最终还是利用原生的方式来解决这个问题,在WHERE...IN中将IN中的所有需要更新或者需要删除的数据生成参数的方式来解决即可,请往下看。

复制代码
        public async Task<bool> UpdateStatus(string idsStr)
        {
            var ids = idsStr.Split(',');

            var parms = ids.Select((s, i) => "@p" + i.ToString()).ToArray();

            var inclause = string.Join(",", parms);

            var parameters = new SqlParameter[parms.Length];

            for (int i = 0; i < ids.Length; i++)
            {
                parameters[i] = new SqlParameter()
                {
                    Value = ids[i],
                    ParameterName = parms[i],
                    SqlDbType = SqlDbType.VarChar,
                    Size = 50
                };
            }

            var effectRows = await _efCoreContext.Database.ExecuteSqlCommandAsync($"UPDATE dbo.Blog SET [Status] = 1 WHERE Id in({inclause})", CancellationToken.None, parameters);
            if (effectRows > 0)
            {
                return true;
            }
            return false;
        }
复制代码

 接下来进行调用更新:

复制代码
        [HttpGet("[action]")]
        public async Task<IActionResult> Index()
        {
            var ids = "2,4,6,7";
            var result = await _blogRepository.UpdateStatus(ids);

            return Ok();
        }
复制代码

数据库原始数据如下:

至此成功进行更新,上述代码则无需一一进行解释,简单易懂。为了方便调用,对于利用WHERE...IN利用进行批量更新或删除将其进行如下封装。

第一步:构造WHERE...IN中的参数

复制代码
        public static string BuildWhereInClause<T>(string partialClause, string paramPrefix, IEnumerable<T> parameters)
        {
            string[] parameterNames = parameters.Select(
                (paramText, paramNumber) => "@" + paramPrefix + paramNumber.ToString())
                .ToArray();

            string inClause = string.Join(",", parameterNames);
            string whereInClause = string.Format(partialClause.Trim(), inClause);

            return whereInClause;
        }
复制代码

第二步:构造参数化Parameter

复制代码
        public static SqlParameter[] Parameter<T>(string paramPrefix, IEnumerable<T> parameters)
        {
            string[] parameterValues = parameters.Select((paramText) => paramText.ToString()).ToArray();

            string[] parameterNames = parameterValues.Select(
                (paramText, paramNumber) => "@" + paramPrefix + paramNumber.ToString()
                ).ToArray();
            var param = new SqlParameter[parameterNames.Length];
            for (int i = 0; i < parameterNames.Length; i++)
            {
                param[i] = new SqlParameter()
                {
                    Value = parameterValues[i],
                    ParameterName = parameterNames[i],
                    SqlDbType = SqlDbType.VarChar
                };
            }

            return param;
        }
复制代码

最终定义一个静态类来调用如上两个方法:

复制代码
    public static class SqlWhereInParameterBuilder
    {
        public static string BuildWhereInClause<T>(string partialClause, string paramPrefix, IEnumerable<T> parameters)
        {
            string[] parameterNames = parameters.Select(
                (paramText, paramNumber) => "@" + paramPrefix + paramNumber.ToString())
                .ToArray();

            string inClause = string.Join(",", parameterNames);
            string whereInClause = string.Format(partialClause.Trim(), inClause);

            return whereInClause;
        }

        public static SqlParameter[] Parameter<T>(string paramPrefix, IEnumerable<T> parameters)
        {
            string[] parameterValues = parameters.Select((paramText) => paramText.ToString()).ToArray();

            string[] parameterNames = parameterValues.Select(
                (paramText, paramNumber) => "@" + paramPrefix + paramNumber.ToString()
                ).ToArray();
            var param = new SqlParameter[parameterNames.Length];
            for (int i = 0; i < parameterNames.Length; i++)
            {
                param[i] = new SqlParameter()
                {
                    Value = parameterValues[i],
                    ParameterName = parameterNames[i],
                    SqlDbType = SqlDbType.VarChar
                };
            }

            return param;
        }
    }
复制代码

此时上述调用则进行如下调用:

            var sql = SqlWhereInParameterBuilder.BuildWhereInClause("UPDATE dbo.Blog SET [Status] = 1 WHERE Id in({0})", "Id", ids);
            var parameters = SqlWhereInParameterBuilder.Parameter("id", ids);
            var effectRows = await _efCoreContext.Database.ExecuteSqlCommandAsync(sql, CancellationToken.None, parameters);

一切从简,想要批量删除或者更新一步到位,你get到没有!遗留一个问题,上述只是针对单表而言,如果是多表,还有其他判断条件的参数,那么上述方法则不再适用,那又该如何改造呢?容我想想!

彩蛋

EntityFramework Core Shadow Property(狭隘属性)

在EF Core系列中介绍过EF Core中几个新特性比如可选键作为除主键外的唯一约束,BackFileds,关于BackFieds未曾用到也差不多忘记了,本节我们介绍一下EF Core漏掉的狭隘属性。

狭隘属性不是实体类的一部分,所以不存在于实体类中但是存在于实体模型中,那么到底该如何使用狭隘属性呢?使用狭隘属性主要在以下两个场景。

(1)当不想对实体类作出更改,但是需要添加一些字段到实体模型中。

(2)明确知道该属性是上下文中的一部分,但是不希望暴露这些属性。

例如在Blog实体类中存在如何字段和导航属性。

复制代码
    public class Blog : IEntityBase
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
        public byte Status { get; set; }
        public IEnumerable<Post> Posts { get; set; }
    }
复制代码

常见场景:现在我们需要添加一个属性创建时间作为狭隘属性,此创建时间只有在实体添加状态时才有其值,其他状态值不发生改变且无需对外暴露,此时我们在映射中进行配置保持实体类洁净,如下:

复制代码
        public override void Map(EntityTypeBuilder<Blog> b)
        {
            b.ToTable("Blog");
           
            b.HasKey(k => k.Id);

            b.Property(p => p.Url);
            b.Property(p => p.Name);
            b.Property(p => p.Status).HasColumnType("TINYINT").IsRequired();

            b.Property<DateTime>("CreatedTime");
        }
复制代码

那么如何对CreatedTime进行设置值和获取值呢?Change Tracker API负责维护狭隘属性,当我们创建Blog时为其狭隘属性赋值,如下:

复制代码
        public async Task<bool> Create()
        {
            var blog = new Blog() { Name = "Jeffcky", Status = 0, Url = "http://www.cnblogs.com/CreateMyself" };

            _efCoreContext.Entry(blog).Property("CreatedTime").CurrentValue = DateTime.Now;

            if (await _efCoreContext.SaveChangesAsync(CancellationToken.None) > 0)
            {
                return true;
            }
            return false;
        }
复制代码

由于对于大部分情况下都有其创建时间这一列,我们放在SaveChanges方法中并将其重写,如下:

复制代码
        public override int SaveChanges()
        {
            var modifiedEntries = ChangeTracker
                                .Entries().Where(x => x.State == EntityState.Added);

            foreach (var item in modifiedEntries)
            {
                item.Property("CreatedTime").CurrentValue = DateTime.Now;
            }
            return base.SaveChanges();
        }

        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            var modifiedEntries = ChangeTracker
                                .Entries().Where(x => x.State == EntityState.Added);

            foreach (var item in modifiedEntries)
            {
                item.Property("CreatedTime").CurrentValue = DateTime.Now;
            }
            return await base.SaveChangesAsync();
        }
复制代码

此时创建Blog则改写为如下:

复制代码
        public async Task<bool> Create()
        {
            var blog = new Blog() { Name = "Jeffcky", Status = 0, Url = "http://www.cnblogs.com/CreateMyself" };

            _efCoreContext.Add(blog);

            if (await _efCoreContext.SaveChangesAsync(CancellationToken.None) > 0)
            {
                return true;
            }
            return false;
        }
复制代码

 

那么问题来了,如果配置的狭隘属性在实体类中已存在那么是否会抛出异常呢?不会,自动将已存在的实体类中同名的名称配置成狭隘属性。当然我们也可以通过如下来起别名:

b.Property<DateTime>("CreatedTime").HasColumnName("CreatedDate");

结论:狭隘属性应是对已存在的实体类添加但是不会去修改狭隘属性值。

那么最后一个问题又来了,在LINQ中如何引用狭隘属性进行查询呢?如下:通过EF.Property<>实现引用狭隘属性:

var cList = _efCoreContext.Blogs
        .OrderBy(b => EF.Property<DateTime>(b, "CreatedTime")).ToList();

总结 

有一段时间没写博客感觉有点生硬,后面会陆陆续续捡起来并将项目中遇到的问题进行总结,如有疑问或言论不对之处,请指教。see u.







本文转自Jeffcky博客园博客,原文链接:http://www.cnblogs.com/CreateMyself/p/7291839.html,如需转载请自行联系原作者

目录
相关文章
|
12月前
|
机器学习/深度学习 人工智能 监控
提升软件质量的关键路径:高效测试策略与实践在软件开发的宇宙中,每一行代码都如同星辰般璀璨,而将这些星辰编织成星系的过程,则依赖于严谨而高效的测试策略。本文将引领读者探索软件测试的奥秘,揭示如何通过精心设计的测试方案,不仅提升软件的性能与稳定性,还能加速产品上市的步伐,最终实现质量与效率的双重飞跃。
在软件工程的浩瀚星海中,测试不仅是发现缺陷的放大镜,更是保障软件质量的坚固防线。本文旨在探讨一种高效且创新的软件测试策略框架,它融合了传统方法的精髓与现代技术的突破,旨在为软件开发团队提供一套系统化、可执行性强的测试指引。我们将从测试规划的起点出发,沿着测试设计、执行、反馈再到持续优化的轨迹,逐步展开论述。每一步都强调实用性与前瞻性相结合,确保测试活动能够紧跟软件开发的步伐,及时适应变化,有效应对各种挑战。
|
8月前
|
安全 Android开发 iOS开发
escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
escrcpy 是一款基于 Scrcpy 的开源项目,使用 Electron 构建,提供图形化界面来显示和控制 Android 设备。它支持 USB 和 Wi-Fi 连接,帧率可达 30-120fps,延迟低至 35-70ms,启动迅速且画质清晰。escrcpy 拥有丰富的功能,包括自动化任务、多设备管理、反向网络共享、批量操作等,无需注册账号或广告干扰。适用于游戏直播、办公协作和教育演示等多种场景,是一款轻量级、高性能的 Android 控制工具。
552 1
|
JSON API Go
Go-Zero从0到1实现微服务项目开发(二)
继续更新GoZero微服务实战系列文章:上一篇被GoZero作者万总点赞了,本文将继续使用 Go-zero 提供的工具和组件,从零开始逐步构建一个基本的微服务项目。手把手带你完成:项目初始化+需求分析+表结构设计+api+rpc+goctl+apifox调试+细节处理。带你实现一个完整微服务的开发。
321 1
|
异构计算 内存技术
FPGA进阶(3):SDRAM读写控制器的设计与验证(一)
FPGA进阶(3):SDRAM读写控制器的设计与验证
382 0
|
12月前
|
网络协议 安全 物联网
探索未来网络:从IPv4到IPv6的演变
本文深入探讨了互联网协议从IPv4向IPv6演进的背景、原因及带来的深远影响。随着物联网、5G等技术的发展,IPv4地址枯竭问题愈发严峻,IPv6应运而生,不仅解决了地址短缺,还提升了网络性能和安全性。通过详细阐述两者在地址空间、报文结构、头部格式等方面的差异,文章强调了IPv6在简化数据包处理流程、增强组播支持等方面的优势。同时,指出IPv6过渡面临的挑战,并展望其在未来互联网中的广阔应用前景。
|
11月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
477 0
|
5G 数据安全/隐私保护
DSSS、CCK 和 OFDM 调制方案
【8月更文挑战第23天】
1665 0
|
Java 数据库 开发者
深入剖析 SpringBoot 的 SPI 机制
【8月更文挑战第10天】在软件开发中,SPI(Service Provider Interface)机制是一种重要的服务发现和加载机制,尤其在构建模块化、可扩展的系统时尤为重要。SpringBoot作为Spring家族的一员,其内置的SPI机制不仅继承了Java SPI的设计思想,还进行了优化和扩展,以适应Spring Boot特有的需求。本文将深入剖析SpringBoot中的SPI机制,揭示其背后的原理与应用。
372 7
|
关系型数据库 MySQL Java
收藏了许久的(快捷键指定):win系统的dos命令
本文收集了一系列Windows系统中的DOS命令快捷键和实用技巧,包括打开系统属性、运行窗口、文件管理、锁定桌面、查看系统信息、配置系统启动模式、系统服务管理、注册表编辑、共享路径访问、环境变量设置、端口查看、文件操作等,旨在提高用户在使用Windows系统时的效率和便利性。
307 0
|
数据采集 DataWorks 安全
DataWorks产品使用合集之如何实现数据过滤
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。