hibernate乐观锁_Hibernate Collection乐观锁定

hibernate乐观锁

介绍

Hibernate提供了一种乐观的锁定机制 ,即使长时间通话也可以防止更新丢失 。 结合实体存储,跨越多个用户请求(扩展的持久性上下文或分离的实体),Hibernate可以保证应用程序级的可重复读取 。

脏检查机制检测实体状态更改并增加实体版本。 尽管始终考虑基本属性更改,但是Hibernate集合在这方面更加微妙。

拥有与反向收藏

在关系数据库中,两个记录通过外键引用关联。 在这种关系中,引用记录是父记录,而引用行(外键侧)是子记录。 非空外键只能引用现有的父记录。

在面向对象的空间中,可以在两个方向上表示这种关联。 我们可以从孩子到父母有一对多的引用,而父母也可以有一对多的孩子集合。

因为双方都有可能控制数据库外键状态,所以我们必须确保只有一方是此关联的所有者。 仅拥有方状态更改会传播到数据库。 非持有端历来称为侧。

接下来,我将描述对该关联进行建模的最常用方法。

单向父项拥有子项关联映射

只有父方具有@OneToMany非逆子级集合。 子实体根本不引用父实体。

@Entity(name = "post")
public class Post {...@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)private List<Comment> comments = new ArrayList<Comment>();...
}

单向父-子-子-子组件关联映射映射

子端不一定总是必须是实体,我们可以将其建模为组件类型 。 一个Embeddable对象(组件类型)可能同时包含基本类型和关联映射,但永远不能包含@Id。 可嵌入对象及其拥有的实体将被持久保存/删除。

父级具有@ElementCollection子级关联。 子实体只能通过不可查询的特定于 Hibernate的@Parent批注来引用父实体。

@Entity(name = "post")
public class Post {...@ElementCollection@JoinTable(name = "post_comments", joinColumns = @JoinColumn(name = "post_id"))@OrderColumn(name = "comment_index")private List<Comment> comments = new ArrayList<Comment>();...public void addComment(Comment comment) {comment.setPost(this);comments.add(comment);}
}	@Embeddable
public class Comment {...@Parentprivate Post post;...
}

双向父子侧子关联映射

父级是拥有方,因此它有一个@OneToMany非逆(不包含mappingBy指令)子级集合。 子实体通过@ManyToOne关联引用父实体,该关联既不可插入也不可更新:

@Entity(name = "post")
public class Post {...@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)private List<Comment> comments = new ArrayList<Comment>();...public void addComment(Comment comment) {comment.setPost(this);comments.add(comment);}
}	@Entity(name = "comment")
public class Comment {...@ManyToOne@JoinColumn(name = "post_id", insertable = false, updatable = false)private Post post;...
}

双向儿童拥有侧-父母关联映射

子实体通过引用父实体@ManyToOne协会和家长有一个的mappedBy @OneToMany孩子集合。 父侧是反侧,因此仅@ManyToOne状态更改会传播到数据库。

即使只有一个拥有的一方,通过使用add / removeChild()方法使双方保持同步始终是一个好习惯。

@Entity(name = "post")
public class Post {...@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "post")private List<Comment> comments = new ArrayList<Comment>();...public void addComment(Comment comment) {comment.setPost(this);comments.add(comment);}
}	@Entity(name = "comment")
public class Comment {...@ManyToOneprivate Post post;	...
}

单向儿童拥有侧父母关系映射

子实体通过@ManyToOne关联引用父实体。 父级没有@OneToMany子级集合,因此子级实体成为所有者。 此关联映射类似于关系数据外键链接。

@Entity(name = "comment")
public class Comment {...@ManyToOneprivate Post post;	...
}

集合版本控制

JPA 2.1规范的3.4.2部分将乐观锁定定义为:

将对象写入数据库时​​,持久性提供程序运行时会更新version属性。 版本检查中包括所有非关系字段和适当的关系以及实体所拥有的所有关系[35]。

[35]这包括在联接表中维护的拥有的关系

注意:只有拥有方的子级集合可以更新父级版本。

测试时间

让我们测试一下父子关联类型如何影响父版本。 因为我们对子级集合的脏检查感兴趣,所以将跳过单向的子级拥有方-父级关联,因为在这种情况下,父级不包含子级集合。

测试用例

以下测试用例将用于所有集合类型用例:

protected void simulateConcurrentTransactions(final boolean shouldIncrementParentVersion) {final ExecutorService executorService = Executors.newSingleThreadExecutor();doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {try {P post = postClass.newInstance();post.setId(1L);post.setName("Hibernate training");session.persist(post);return null;} catch (Exception e) {throw new IllegalArgumentException(e);}}});doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(final Session session) {final P post = (P) session.get(postClass, 1L);try {executorService.submit(new Callable<Void>() {@Overridepublic Void call() throws Exception {return doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session _session) {try {P otherThreadPost = (P) _session.get(postClass, 1L);int loadTimeVersion = otherThreadPost.getVersion();assertNotSame(post, otherThreadPost);assertEquals(0L, otherThreadPost.getVersion());C comment = commentClass.newInstance();comment.setReview("Good post!");otherThreadPost.addComment(comment);_session.flush();if (shouldIncrementParentVersion) {assertEquals(otherThreadPost.getVersion(), loadTimeVersion + 1);} else {assertEquals(otherThreadPost.getVersion(), loadTimeVersion);}return null;} catch (Exception e) {throw new IllegalArgumentException(e);}}});}}).get();} catch (Exception e) {throw new IllegalArgumentException(e);}post.setName("Hibernate Master Class");session.flush();return null;}});
}

单向父母所有子女的关联测试

#create tables
Query:{[create table comment (id bigint generated by default as identity (start with 1), review varchar(255), primary key (id))][]} 
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]} 
Query:{[create table post_comment (post_id bigint not null, comments_id bigint not null, comment_index integer not null, primary key (post_id, comment_index))][]} 
Query:{[alter table post_comment add constraint UK_se9l149iyyao6va95afioxsrl  unique (comments_id)][]} 
Query:{[alter table post_comment add constraint FK_se9l149iyyao6va95afioxsrl foreign key (comments_id) references comment][]} 
Query:{[alter table post_comment add constraint FK_6o1igdm04v78cwqre59or1yj1 foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_1_0_, entityopti0_.name as name2_1_0_, entityopti0_.version as version3_1_0_ from post entityopti0_ where entityopti0_.id=?][1]} #insert comment in secondary transaction
#optimistic locking post version update in secondary transaction
Query:{[insert into comment (id, review) values (default, ?)][Good post!]} 
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate training,1,1,0]} 
Query:{[insert into post_comment (post_id, comment_index, comments_id) values (?, ?, ?)][1,0,1]} #optimistic locking exception in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]}
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.EntityOptimisticLockingOnUnidirectionalCollectionTest$Post#1]

单向父-子-子组件关联测试

#create tables
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]} 
Query:{[create table post_comments (post_id bigint not null, review varchar(255), comment_index integer not null, primary key (post_id, comment_index))][]} 
Query:{[alter table post_comments add constraint FK_gh9apqeduab8cs0ohcq1dgukp foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_0_0_, entityopti0_.name as name2_0_0_, entityopti0_.version as version3_0_0_ from post entityopti0_ where entityopti0_.id=?][1]} 
Query:{[select comments0_.post_id as post_id1_0_0_, comments0_.review as review2_1_0_, comments0_.comment_index as comment_3_0_ from post_comments comments0_ where comments0_.post_id=?][1]} #insert comment in secondary transaction
#optimistic locking post version update in secondary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate training,1,1,0]} 
Query:{[insert into post_comments (post_id, comment_index, review) values (?, ?, ?)][1,0,Good post!]} #optimistic locking exception in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]} 
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.EntityOptimisticLockingOnComponentCollectionTest$Post#1]

双向父母拥有-子-孩子关联测试

#create tables
Query:{[create table comment (id bigint generated by default as identity (start with 1), review varchar(255), post_id bigint, primary key (id))][]} 
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]} 
Query:{[create table post_comment (post_id bigint not null, comments_id bigint not null)][]} 
Query:{[alter table post_comment add constraint UK_se9l149iyyao6va95afioxsrl  unique (comments_id)][]} 
Query:{[alter table comment add constraint FK_f1sl0xkd2lucs7bve3ktt3tu5 foreign key (post_id) references post][]} 
Query:{[alter table post_comment add constraint FK_se9l149iyyao6va95afioxsrl foreign key (comments_id) references comment][]} 
Query:{[alter table post_comment add constraint FK_6o1igdm04v78cwqre59or1yj1 foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_1_0_, entityopti0_.name as name2_1_0_, entityopti0_.version as version3_1_0_ from post entityopti0_ where entityopti0_.id=?][1]} 
Query:{[select comments0_.post_id as post_id1_1_0_, comments0_.comments_id as comments2_2_0_, entityopti1_.id as id1_0_1_, entityopti1_.post_id as post_id3_0_1_, entityopti1_.review as review2_0_1_, entityopti2_.id as id1_1_2_, entityopti2_.name as name2_1_2_, entityopti2_.version as version3_1_2_ from post_comment comments0_ inner join comment entityopti1_ on comments0_.comments_id=entityopti1_.id left outer join post entityopti2_ on entityopti1_.post_id=entityopti2_.id where comments0_.post_id=?][1]} #insert comment in secondary transaction
#optimistic locking post version update in secondary transaction
Query:{[insert into comment (id, review) values (default, ?)][Good post!]} 
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate training,1,1,0]} 
Query:{[insert into post_comment (post_id, comments_id) values (?, ?)][1,1]} #optimistic locking exception in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]} 
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.EntityOptimisticLockingOnBidirectionalParentOwningCollectionTest$Post#1]

双向儿童拥有侧-父母关联测试

#create tables
Query:{[create table comment (id bigint generated by default as identity (start with 1), review varchar(255), post_id bigint, primary key (id))][]} 
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]} 
Query:{[alter table comment add constraint FK_f1sl0xkd2lucs7bve3ktt3tu5 foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_1_0_, entityopti0_.name as name2_1_0_, entityopti0_.version as version3_1_0_ from post entityopti0_ where entityopti0_.id=?][1]} #insert comment in secondary transaction
#post version is not incremented in secondary transaction
Query:{[insert into comment (id, post_id, review) values (default, ?, ?)][1,Good post!]} 
Query:{[select count(id) from comment where post_id =?][1]} #update works in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]}

否决默认集合版本控制

如果默认的拥有方集合版本控制不适合您的用例,则始终可以使用Hibernate @OptimisticLock注释来取代它。

让我们废除双向parent-owning-side-child关联的默认父版本更新机制:

@Entity(name = "post")
public class Post {...@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)@OptimisticLock(excluded = true)private List<Comment> comments = new ArrayList<Comment>();...public void addComment(Comment comment) {comment.setPost(this);comments.add(comment);}
}	@Entity(name = "comment")
public class Comment {...@ManyToOne@JoinColumn(name = "post_id", insertable = false, updatable = false)private Post post;...
}

这次,子级集合更改不会触发父版本更新:

#create tables
Query:{[create table comment (id bigint generated by default as identity (start with 1), review varchar(255), post_id bigint, primary key (id))][]} 
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]} 
Query:{[create table post_comment (post_id bigint not null, comments_id bigint not null)][]} 
Query:{[alter table post_comment add constraint UK_se9l149iyyao6va95afioxsrl  unique (comments_id)][]} 
Query:{[alter table comment add constraint FK_f1sl0xkd2lucs7bve3ktt3tu5 foreign key (post_id) references post][]} 
Query:{[alter table post_comment add constraint FK_se9l149iyyao6va95afioxsrl foreign key (comments_id) references comment][]} 
Query:{[alter table post_comment add constraint FK_6o1igdm04v78cwqre59or1yj1 foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_1_0_, entityopti0_.name as name2_1_0_, entityopti0_.version as version3_1_0_ from post entityopti0_ where entityopti0_.id=?][1]} 
Query:{[select comments0_.post_id as post_id1_1_0_, comments0_.comments_id as comments2_2_0_, entityopti1_.id as id1_0_1_, entityopti1_.post_id as post_id3_0_1_, entityopti1_.review as review2_0_1_, entityopti2_.id as id1_1_2_, entityopti2_.name as name2_1_2_, entityopti2_.version as version3_1_2_ from post_comment comments0_ inner join comment entityopti1_ on comments0_.comments_id=entityopti1_.id left outer join post entityopti2_ on entityopti1_.post_id=entityopti2_.id where comments0_.post_id=?][1]} #insert comment in secondary transaction
Query:{[insert into comment (id, review) values (default, ?)][Good post!]} 
Query:{[insert into post_comment (post_id, comments_id) values (?, ?)][1,1]} #update works in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]}

结论

了解各种建模结构如何影响并发模式非常重要。 递增父版本号时,将考虑拥有方集合的更改,您始终可以使用@OptimisticLock批注绕过它。

  • 代码可在GitHub上获得 。

翻译自: https://www.javacodegeeks.com/2014/11/hibernate-collections-optimistic-locking.html

hibernate乐观锁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/339058.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

turtle fillcolor_使Python中的turtle模块画图两只小羊

import turtle import time import math as m class Sheep(object):def __init__(self,xsize):self.t turtle.Turtle()self.xsize xsizet self.t# 画笔设置t.screen.screensize(canvwidth1000,canvheight500,bgwhite)t.pensize(2)t.speed(10)# t.hideturtle()#初始化画笔位置…

计算机鼠标介绍教学反思,《玩转鼠标》教学反思

《玩转鼠标》教学反思《玩转鼠标》是浙江摄影出版社出版的小学信息技术三年级第一单元中的第3课。这节课其实是上一节课的延伸&#xff0c;是一节复习课&#xff0c;起到巩固强化的目的&#xff0c;主要任务是让学生熟练掌握鼠标的基本操作方法&#xff0c;学会软件的打开和关闭…

openshift_在OpenShift上托管的WildFly实例上进行Arquillian测试

openshift技术提示&#xff03;54解释了如何为现有Java EE项目启用Arquillian。 在该技巧中&#xff0c;测试是针对本地安装的WildFly服务器运行的。 如果此WildFly实例在OpenShift上运行&#xff0c;那么同一个适配器也可以工作吗&#xff1f; 没有&#xff01; 因为与xlocal…

C语言实现魔方阵代码及解析

问题描述编写程序&#xff0c;实现如下表所示的5-魔方阵。172418152357141646132022101219213111825295-魔方阵问题分析所谓“n-魔方阵”&#xff0c;指的是使用1〜n2共n2个自然数排列成一个nn的方阵&#xff0c;其中n为奇数&#xff1b;该方阵的每行、每列及对角线元素之和都相…

600度近视眼恢复方法_近视孩子的家长看看:600度以上近视可致盲,不花钱恢复视力法...

现在孩子近视早已经不是什么新鲜事了&#xff0c;一个班级里面有3分之一的孩子都近视&#xff0c;数据显示&#xff1a;目前中国学生近视发病率接近60%&#xff0c;居世界首位&#xff01;中小学生近视率已达34.6%&#xff0c;高中近视率已达70%。患者人数超过六千万&#xff0…

xbox360 功率测试软件,【外星人 Alpha ASM100-1580 游戏主机使用总结】性能|电压|功耗|跑分_摘要频道_什么值得买...

外星人 Alpha ASM100-1580 游戏主机使用总结(性能|电压|功耗|跑分)在讨论性能之前&#xff0c;再最后看一看电源。NUC电源外置是必然&#xff0c;除了体积之外&#xff0c;还要考虑散热&#xff0c;所以&#xff0c;电源外置至少在目前来看还是最优解。这款电源是台达电子代工的…

openfire消息通知推送_微信小游戏内测「订阅消息」能力,这是召回用户的「大杀器」吗?...

作者&#xff1a;蒋鸿昌本文来源于「知晓程序」公众号。知晓云后端云服务&#xff0c;让你的小程序开发快人一步&#xff0c;添加「minsupport3」了解详情。知晓云​cloud.minapp.com一位投资人曾把最近 3 年微信公开课 Pro 版上张小龙的演讲称为「小程序 3 篇」&#xff1a;如…

api 数据gzip压缩_如何使用GZip和Jersey压缩Java REST API中的响应

api 数据gzip压缩在某些情况下&#xff0c;您的REST api会提供非常长的响应&#xff0c;我们都知道移动设备/网络上的传输速度和带宽仍然非常重要。 我认为这是开发支持移动应用程序的REST api时需要解决的第一个性能优化点。 你猜怎么了&#xff1f; 因为响应是文本&#xff0…

c语言心形告白代码实现

1.彩色告白includeincludeincludeincludedefine U 0.1define V 0.053void SetColor(unsigned short ForeColor,unsigned short BackGroundColor){HANDLE hConGetStdHandle(STD_OUTPUT_HANDLE);SetConsoleTextAttribute(hCon,(ForeColor)|(BackGroundColor16));}int main(){int …

obs可以推到中转服务器吗,能否使用OBS(Open Broadcaster Software)、FMLE(Flash Media Live Encoder)等第三方软件进行推流?...

"可以。请参考以下方法进行设置。使用OBS进行直播设置 > 串流 > 串流类型&#xff1a;自定义流媒体服务器URL&#xff1a;填写网易服务器提供的推流地址中&#xff0c;从头到live的部分&#xff0c;例如&#xff1a;rtmp://p12345678.live.126.net/live流密钥&#…

mysql 隐藏中间四位_MySQL知识体系——索引

本文直切主题&#xff0c;针对InnoDB引擎描述索引及优化策略。在开始之前&#xff0c;需要读者了解&#xff1a;二叉查找树(包括2-3查找树、红黑树等数据结构)MySQL的InnoDB引擎基础知识索引初探要了解索引&#xff0c;当然要了解其数据结构。树有很多应用&#xff0c;流行的用…

python中属于私有属性的是_Python中的实例属性和私有属性

相关知识点 实例属性 实例属性和类属性的区别在于实例属性定义在类的__init__()魔法方法中&#xff0c;而类属性定义在类下&#xff1a; 1 classC:2 b 1 #类属性 3 4 def __init__(self):5 self.x None #实例属性 对于类属性这里不做说明&#xff0c;主要描述实例属性&#x…

osgi cxf_所有OSGi套件的通用CXF请求拦截器

osgi cxf几个月以来&#xff0c;我一直在研究Apache CXF&#xff0c;Karaf和Felix&#xff0c;我发现所有这些捆绑技术都非常有趣。 在处理一些用例时&#xff0c;我陷入一种情况&#xff0c;即我只需要一个Interceptor即可在发送到Karaf应用程序下部署的任何捆绑包的每个HTTP请…

听说,高手都用记事本写C语言代码?

坊间传闻高手都喜欢用记事本写代码&#xff0c;那么问题来了&#xff0c;我们以C语言为例&#xff0c;如何用记事本编译运行呢&#xff1f;其实&#xff0c;最简单的方式就是安装GCC编译器&#xff0c;在记事本编写C语言程序&#xff0c;然后再在命令行用GCC编译运行。下面&…

精简指令和复杂指令计算机,CPU精简指令集和复杂指令集的区别

经常看到计算机课程上介绍PowerPC是采用精简指令集的CPU&#xff0c;酷睿CPU是复杂指令集。那么到底精简指令集和复杂指令集有什么区别呢&#xff1f;以下内容摘自百度百科&#xff1a;在计算机指令系统的优化发展过程中&#xff0c;出现过两个截然不同的优化方向&#xff1a;C…

均值差异大但是t检验不显著_T检验原理及介绍

T检验原理及应用介绍一、T检验的应用场景&#xff1a;应用场景&#xff1a;可以用于研究工艺更改前后对于产品质量是否有显著提高&#xff1b;研究不同的热处理温度对于产品性能&#xff0c;晶粒度等是否有明显效果&#xff1b;研究不同的设备或人员对于产品质量的影响是否有差…

javafx ui_调用以验证JavaFX UI的响应能力

javafx ui最近&#xff0c;吉姆韦弗&#xff08;Jim Weaver&#xff09;在他的Surface Pro上安装了我的小图片索引应用“ picmodo”作为演示&#xff0c;图形用户界面&#xff08;GUI&#xff09;变成了垃圾。 显然&#xff0c;Windows Tablet上JavaFX的基本字体大小很高&…

python中mean的用法_Python statistics mean()用法及代码示例

先决条件&#xff1a;统计函数简介 在数据分析和统计方面&#xff0c;Python是一种非常流行的语言。幸运的是&#xff0c;Python3提供了statistics模块&#xff0c;它具有非常有用的功能&#xff0c;例如mean()&#xff0c;median()&#xff0c;mode()等等 mean()函数可用于计算…

C语言多文件编程基本格式

1、背景&#xff1a;用一个丢骰子的简单案例熟悉了C语言多文件编程该咋写2、格式&#xff08;1&#xff09;主函数文件main.c//文件头part1:所有要使用的函数#include ;#include ;#include ;//文件头part2:放所有函数原型的文件#inlucde "main.h"//文件头part3&#…

微软project服务器搭建,Project Professional 版本(Project Server 2010 设置)

Project Professional 版本利用“Project Professional 版本”&#xff0c;您可以指定将能够连接到 Project Server 2010 环境的 Project Professional 客户端的版本(内部版本号)。可通过此设置确保与服务器的 Project Professional 客户端连接都处于所需的基本级别。例如&…