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

封装neo4j的持久层和服务层

目录

持久层

mp

模仿:

1.抽取出通用的接口类

2.创建自定义的repository接口

服务层

 mp

模仿:

1.抽取出一个IService通用服务类

2.创建ServiceImpl类实现IService接口

3.自定义的服务接口

4.创建自定义的服务类

工厂模式

为什么可以使用工厂模式

如何使用工厂模式

枚举类

工厂类

第一种方式:使用switch方式来判断要返回哪一种服务类型

第二种:通过反射来判断

使用工厂模式


这里我来分享以下参考mybatis-plus的格式来封装neo4j的持久层,和设计出一个服务层

持久层

mp

这里我们先看以下mp持久层的是如何操作的

可以发现mp抽取出了一个通用的BaseMapper接口,并使用泛型T代指对实体类对象的统一操作

public interface BaseMapper<T> extends Mapper<T> {int insert(T entity);int deleteById(Serializable id);int deleteById(T entity);
...
}

然后我们就可以创建一个mapper类,并继承自BaseMapper接口,然后泛型指定为PayChannelEntity实体类,这个实体类就是使用@TableName注解与数据表一一对应的实体类

@Mapper
public interface PayChannelMapper extends BaseMapper<PayChannelEntity> {}

总结,mp通过抽取出一个BaseMapper通用的接口,然后让我们自己创建的mapper类去继承BaseMapper,并指定要操作的实体类,这样一来,大多数的CURD操作我们就不用自己实现,并且我们自己创建的mapper类使用@Mapper注解,这个注解可以让Spring扫描并为其创建一个代理对象。

模仿:

1.抽取出通用的接口类

这个类继承Neo4jRepository接口,这个接口有neo4j提供的大多数CURD方法

并且也使用泛型,并且规定这个T泛型必须继承自BaseEntity类,还有另一个泛型ID,表示id属性的类型

这里使用@NoRepositoryBean注解的作用是不用给这个通用接口类生成bean,因为继承Neo4jRepository接口的接口都会被扫描生成代理对象,这里没必要生成,mp是使用了@Mapper注解才会生成代理对象

/*** Repository基本方法的实现**/
@NoRepositoryBean//让这个类不生成bean
public interface BaseRepository<T extends BaseEntity, ID> extends Neo4jRepository<T, ID> {/*** 根据业务id查询节点数据** @param bid 业务id* @return 节点数据*/Optional<T> findByBid(Long bid);/*** 根据业务id删除节点数据** @param bid 业务id* @return 删除的数据数量*/Long deleteByBid(Long bid);
}

2.创建自定义的repository接口

这里指定泛型为AgencyEntity类,和id属性为Long类

AgencyEntity类是与neo4j中AGENCY标签一一对应的实体类

通过实现getAgencyType()抽象方法来区别不同的机构实体类

/*** 网点数据操作*/
public interface AgencyRepository extends BaseRepository<AgencyEntity, Long> {}@Node("AGENCY")
@Data
@ToString(callSuper = true)
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
public class AgencyEntity extends BaseEntity {@Overridepublic OrganTypeEnum getAgencyType() {return OrganTypeEnum.AGENCY;}
}@Data
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public abstract class BaseEntity {@Id@GeneratedValue@ApiModelProperty(value = "Neo4j ID", hidden = true)private Long id;@ApiModelProperty(value = "父节点id", required = true)private Long parentId;@ApiModelProperty(value = "业务id", required = true)private Long bid;@ApiModelProperty(value = "名称", required = true)private String name;@ApiModelProperty(value = "负责人", required = true)private String managerName;@ApiModelProperty(value = "电话", required = true)private String phone;@ApiModelProperty(value = "地址", required = true)private String address;@ApiModelProperty(value = "位置坐标, x: 纬度,y: 经度", required = true)private Point location;@ApiModelProperty(value = "是否可用", required = true)private Boolean status;@ApiModelProperty(value = "扩展字段,以json格式存储")private String extra;//机构类型public abstract OrganTypeEnum getAgencyType();}

还有另外两个持久层接口类 

服务层

 mp

这里看一下mp的服务层实现

可以发现mp的服务层也抽取出了一个通用的服务接口类,也使用了泛型T来代指操作的实体类

public interface IService<T> {int DEFAULT_BATCH_SIZE = 1000;default boolean save(T entity) {return SqlHelper.retBool(this.getBaseMapper().insert(entity));}@Transactional(rollbackFor = {Exception.class})default boolean saveBatch(Collection<T> entityList) {return this.saveBatch(entityList, 1000);}boolean saveBatch(Collection<T> entityList, int batchSize);
BaseMapper<T> getBaseMapper();Class<T> getEntityClass();
...
}

这个接口类是没有实现的,如果我们要自己创建一个服务类去实现这个接口,是十分麻烦的,所以mp提供了一个ServiceImpl类对这个接口进行了实现

这个实现类使用了一个泛型M代指继承了BaseMapper接口的类,还有泛型T代指实体类

这样一来就可以使用M来实现IService接口的所有方法操作了。

public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {protected Log log = LogFactory.getLog(this.getClass());@Autowiredprotected M baseMapper;protected Class<T> entityClass = this.currentModelClass();protected Class<M> mapperClass = this.currentMapperClass();public ServiceImpl() {}public M getBaseMapper() {return this.baseMapper;}public Class<T> getEntityClass() {return this.entityClass;}/** @deprecated */@Deprecatedprotected boolean retBool(Integer result) {return SqlHelper.retBool(result);}protected Class<M> currentMapperClass() {return ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 0);}
...
}

自定义的服务接口类,用来拓展自己特有的方法

public interface PayChannelService extends IService<PayChannelEntity> {}

自定义的服务类,把对应的mapper接口类型,和需要操作的实体类作为泛型传入

@Service
public class PayChannelServiceImpl extends ServiceImpl<PayChannelMapper, PayChannelEntity> implements PayChannelService {
}

模仿:

1.抽取出一个IService通用服务类

使用泛型T代指需要操作的实体类

public interface IService<T extends BaseEntity> {/*** 根据业务id查询数据** @param bid 业务id* @return 节点数据*/T queryByBid(Long bid);/*** 新增节点** @param t 节点数据* @return 新增的节点数据*/T create(T t);/*** 更新节点** @param t 节点数据* @return 更新的节点数据*/T update(T t);/*** 根据业务id删除数据** @param bid 业务id* @return 是否删除成功*/Boolean deleteByBid(Long bid);}

2.创建ServiceImpl类实现IService接口

这里使用R泛型代指repostory接口类,这样一来就可以使用R类型的repostory接口来对方法进行实现。

/*** 基础服务的实现*/
public class ServiceImpl<R extends BaseRepository, T extends BaseEntity> implements IService<T> {@Autowiredprivate R repository;@Overridepublic T queryByBid(Long bid) {return (T) this.repository.findByBid(bid).orElse(null);}@Overridepublic T create(T t) {t.setId(null);//id由neo4j自动生成return (T) this.repository.save(t);}@Overridepublic T update(T t) {//先查询,再更新T tData = this.queryByBid(t.getBid());if (ObjectUtil.isEmpty(tData)) {return null;}BeanUtil.copyProperties(t, tData, CopyOptions.create().ignoreNullValue().setIgnoreProperties("id", "bid"));return (T) this.repository.save(tData);}@Overridepublic Boolean deleteByBid(Long bid) {return this.repository.deleteByBid(bid) > 0;}
}

3.自定义的服务接口

public interface AgencyService extends IService<AgencyEntity> {}public interface OLTService extends IService<OLTEntity> {}
public interface TLTService extends IService<TLTEntity> {}

4.创建自定义的服务类

创建实现类,并把这个类都交给spring管理


@Service
public class AgencyServiceImpl extends ServiceImpl<AgencyRepository, AgencyEntity> implements AgencyService {
}@Service
public class TLTServiceImpl extends ServiceImpl<TLTRepository, TLTEntity>implements TLTService {
}@Service
public class OLTServiceImpl extends ServiceImpl<OLTRepository, OLTEntity>implements OLTService {
}

工厂模式

我们可以使用工厂模式来动态的生成不同的服务实现类供我们使用

为什么可以使用工厂模式

因为所有的服务类都实现了IService这个通用的服务接口,这样一来就可以使用父类引用指向子类对象的多态思想

如何使用工厂模式

我们可以创建一个工厂类,还有一个枚举类,代替不同的机构类型,然后再工厂类的静态方法中根据不同的枚举类的code值来创建对应的服务类并返回。

枚举类

public enum OrganTypeEnum implements BaseEnum {OLT(1, "一级转运中心"),TLT(2, "二级转运中心"),AGENCY(3, "网点");/*** 类型编码*/private final Integer code;/*** 类型值*/private final String value;OrganTypeEnum(Integer code, String value) {this.code = code;this.value = value;}public Integer getCode() {return code;}public String getValue() {return value;}public static OrganTypeEnum codeOf(Integer code) {return EnumUtil.getBy(OrganTypeEnum::getCode, code);}
}

工厂类

第一种方式:使用switch方式来判断要返回哪一种服务类型

根据传入的Integer类型数据,使用枚举类的codeOf方法,创建对应的枚举类对象

然后使用hutool的SpringUtil获取对应服务类的实例bean对象并返回

/*** 根据type选择对应的service返回** @author zzj* @version 1.0*/
public class OrganServiceFactory {public static IService getBean(Integer type) {OrganTypeEnum organTypeEnum = OrganTypeEnum.codeOf(type);if (null == organTypeEnum) {throw new SLException(ExceptionEnum.ORGAN_TYPE_ERROR);}switch (organTypeEnum) {case AGENCY: {return SpringUtil.getBean(AgencyService.class);}case OLT: {return SpringUtil.getBean(OLTService.class);}case TLT: {return SpringUtil.getBean(TLTService.class);}}return null;}}

第二种:通过反射来判断

以上方法我们要通过switch语句来判断是要返回哪一种类型的服务类,那如果枚举类型很多的话,这样是不是冗杂

定义一个接口,在接口里定义能返回对应机构枚举类型的方法

public interface OrganTypeService {/*** 获取对应的机构枚举类型*/OrganTypeEnum getOrganTypeEnum();
}

然后自定义的服务接口都继承这个接口

public interface AgencyService extends IService<AgencyEntity> ,OrganTypeService{}
public interface OLTService extends IService<OLTEntity> ,OrganTypeService{}
public interface TLTService extends IService<TLTEntity>,OrganTypeService{}

服务类实现这个方法,分别返回对应的枚举类型

@Service
public class AgencyServiceImpl extends ServiceImpl<AgencyRepository, AgencyEntity> implements AgencyService {@Overridepublic OrganTypeEnum getOrganTypeEnum() {return OrganTypeEnum.AGENCY;}
}
@Service
public class OLTServiceImpl extends ServiceImpl<OLTRepository, OLTEntity>implements OLTService {@Overridepublic OrganTypeEnum getOrganTypeEnum() {return OrganTypeEnum.OLT;}
}
@Service
public class TLTServiceImpl extends ServiceImpl<TLTRepository, TLTEntity>implements TLTService {@Overridepublic OrganTypeEnum getOrganTypeEnum() {return OrganTypeEnum.TLT;}
}

方法实现

public class OrganServiceFactory {public static IService getBean(Integer type) {OrganTypeEnum organTypeEnum = OrganTypeEnum.codeOf(type);if (null == organTypeEnum) {throw new SLException(ExceptionEnum.ORGAN_TYPE_ERROR);}//先从容器中获取所有的IService类型的bean实例对象Map<String, IService> beans = SpringUtil.getBeansOfType(IService.class);for (Map.Entry<String, IService> entry : beans.entrySet()) {//通过反射执行getOrganTypeEnum()方法Object invoke = ReflectUtil.invoke(entry.getValue(), "getOrganTypeEnum");//如果类型一致就返回对应的服务类实例if(ObjectUtil.equal(invoke,organTypeEnum)){return entry.getValue();}}return null;}}

对比:

第一种方式不用写额外的方法来返回对应的枚举类型,但是需要在工厂的静态方法里来判断枚举类型,如果枚举类型少而且不用经常改变代码,就可以使用这种方式

第二种方式需要写额外的方法来返回对应的枚举类型,但是在工厂方法里就可以使用反射直接执行获取枚举类型的方法来动态获取对应的服务类型。

使用工厂模式

@SpringBootTest
public class Test1 {@Testpublic void testo1(){IService iService = OrganServiceFactory.getBean(1);System.out.println(iService);}
}

成功返回OLT类型的服务实现类 

如果不使用工厂模式,我们就要提前注入这三种类型的服务bean,这样是不是很冗余呢,然后还需要根据需要操作的实体类的不同来判断使用哪一个服务类,十分麻烦

if(type==1) 使用oltService来操作

else if(type==2) ...

    @ResourceOLTService oltService;@ResourceTLTService tltService;@ResourceAgencyService agencyService;

相关文章:

封装neo4j的持久层和服务层

目录 持久层 mp 模仿&#xff1a; 1.抽取出通用的接口类 2.创建自定义的repository接口 服务层 mp 模仿&#xff1a; 1.抽取出一个IService通用服务类 2.创建ServiceImpl类实现IService接口 3.自定义的服务接口 4.创建自定义的服务类 工厂模式 为什么可以使用工厂…...

基于Spring Boot的宠物爱心组织管理系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

error: conflicting types for ‘SSL_SESSION_get_master_key’

$ make make all-am make[1]: Entering directory ‘/home/linuxuser/tor’ CC src/lib/tls/libtor_tls_a-tortls_openssl.o In file included from src/lib/tls/tortls_openssl.c:61: ./src/lib/tls/tortls_internal.h:55:8: error: conflicting types for ‘SSL_SESSION_get_…...

测试狗参加国家超级计算成都中心2024年度用户大会

近日&#xff0c;国家超级计算成都中心举办了“数启新篇算领未来”2024年度用户大会。这场盛会汇聚了来自政府部门、科研院所及企业界的百余位领导专家及用户代表&#xff0c;共同探讨高性能计算在科技创新中的赋能作用&#xff0c;探索超算融合领域的创新发展之路。其中&#…...

从2025年起:数字化建站PHP 8.1应成为建站开发的基准线

在数字化浪潮席卷全球的今天,PHP语言仍然保持着Web开发领域的核心地位。根据W3Techs最新统计,PHP驱动着全球78.9%的已知服务端网站。当时间指向2025年,这个拥有28年历史的编程语言将迎来新的发展里程碑——PHP 8.1版本应成为网站开发的最低基准要求,这不仅是技术迭代的必然…...

飞牛OS与昔映OS深度对比

无论是备份珍贵的照片、视频&#xff0c;搭建个人专属的影视库&#xff0c;还是实现高效的文件共享与协作&#xff0c;NAS 都能成为我们的得力助手。而在众多的 NAS 系统中&#xff0c;飞牛 OS 与昔映 OS 凭借各自的特点&#xff0c;吸引了不少用户的关注。今天&#xff0c;咱们…...

vscode本地和远程对应分支没有同步提交数量

1、问题&#xff1a; 下载了最新的vscode后发现本地分支不显示跟远端分支的提交数量&#xff0c;每次都要手动拉取&#xff0c;如下图 2、解决 在vscode点击左下角设置图标&#xff0c;选择settings&#xff0c;直接搜索git的配置 果然自动拉取的配置设置为false,调整为true即…...

通过docker启用rabbitmq插件

创建文件&#xff0c;docker-compose.yml services:rabbitmq:image: rabbitmq:4.0-managementports:- "5672:5672"- "15672:15672"volumes:- ./data/rabbitmq/data:/var/lib/rabbitmq # 持久化数据- ./data/rabbitmq/plugins/rabbitmq_delayed_message_ex…...

DeepSeek计算机视觉(Computer Vision)基础与实践

计算机视觉(Computer Vision)是人工智能领域的一个重要分支,专注于让计算机理解和处理图像和视频数据。计算机视觉技术广泛应用于图像分类、目标检测、图像分割、人脸识别等场景。DeepSeek提供了强大的工具和API,帮助我们高效地构建和训练计算机视觉模型。本文将详细介绍如…...

哪些专业跟FPGA有关?

FPGA产业作为近几年新兴的技术领域&#xff0c;薪资高、待遇好&#xff0c;吸引了大量的求职者。特别是对于毕业生&#xff0c;FPGA领域的岗位需求供不应求。那么&#xff0c;哪些专业和FPGA相关呢&#xff1f; 哪些专业跟FPGA有关&#xff1f; 微电子学与固体电子学、微电子科…...

【STM32系列】利用MATLAB配合ARM-DSP库设计IIR数字滤波器(保姆级教程)

ps.源码放在最后面 设计FIR数字滤波器可以看这里&#xff1a;利用MATLAB配合ARM-DSP库设计FIR数字滤波器&#xff08;保姆级教程&#xff09; 设计IIR滤波器 MATLAB配置 设计步骤 首先在命令行窗口输入"filterDesigner"&#xff0c;接着就会跳出以下界面&#xf…...

Java每日精进·45天挑战·Day18

一、解码嵌套编码字符串 在编程中&#xff0c;我们经常遇到需要对特定格式的字符串进行解析和解码的任务。今天&#xff0c;我们来探讨一个具体的例子&#xff1a;如何解码一个按照特定规则编码的字符串。这个规则允许字符串中的一部分被重复多次&#xff0c;且这种重复可以嵌…...

C# 中用于比较两个字符串的方法string.Compare

string.Compare 是 C# 中用于比较两个字符串的方法。它返回一个整数&#xff0c;表示两个字符串在字典顺序&#xff08;lexicographical order&#xff09;中的相对关系。这个方法非常有用&#xff0c;尤其是在排序、查找或比较字符串时。 string.Compare 的详细说明 方法签名…...

进阶数据结构——树状数组

前言 看这篇文章前我建议你们先看这个视频还有这个视频&#xff0c;不然你们可能看不懂。 一、树状数组的核心思想与本质 核心思想&#xff1a;树状数组&#xff08;Fenwick Tree&#xff09;是一种用于高效处理前缀和查询和单点更新的数据结构。 本质&#xff1a;通过二进…...

键盘启用触摸板-tips

在日常使用笔记本电脑时&#xff0c;我们会遇到没带鼠标&#xff0c;触摸板关闭的情况&#xff0c;通常情况下&#xff0c;我们习惯通过鼠标点击或触摸屏操作来启用触摸板&#xff0c;但其实通过键盘也能轻松实现这一功能。以下就是一种通过键盘操作启用触摸板的方法&#xff0…...

信息安全之网络安全

网络安全技术是一类包含内容极其广泛的技术&#xff0c;广义上说任何检测、防御和抵制网络攻击的技术都属于网络安全技术&#xff0c;而且很多网络安全技术都是攻击驱动型的。 网络安全大致包含的内容主要有防火墙&#xff0c;入侵检测&#xff0c;漏洞扫描与网络隔离&#xf…...

成都国际数字影像产业园布局者树莓集团,亮相宜宾翠屏招商签约

在商业版图的不断拓展中&#xff0c;树莓集团始终以敏锐的市场洞察力和果敢的决策力占据先机。近期&#xff0c;作为成都国际数字影像产业园的布局者&#xff0c;树莓集团高调亮相宜宾翠屏招商签约盛会&#xff0c;引发行业内外的广泛关注。 宜宾翠屏招商签约盛会&#xff0c;…...

opencascade 获取edge起始点 会出现终点与实际不同的情况

在使用 OpenCASCADE 获取 TopoDS_Edge 的起始点和终点时&#xff0c;可能会出现终点与实际不一致的情况。这通常是由于以下原因导致的&#xff1a; 几何曲线的方向问题&#xff1a;在某些情况下&#xff0c;几何曲线的方向可能与拓扑边的方向不一致&#xff0c;导致通过几何曲线…...

掌握正则表达式_模式匹配的艺术

当然,以下是《掌握正则表达式:模式匹配的艺术》文章内容,使用 Java 正则表达式,并包含丰富的代码示例: 1. 引言 1.1 正则表达式的定义与历史 正则表达式(Regular Expression,简称 regex 或 regexp)是一种用于描述文本模式的强大工具。它最初由数学家 Stephen Kleene…...

【蓝桥】二维DP--摆花

&#x1f4cc;题目描述 &#x1f4cc;解题思路 &#x1f4cc;完整代码 &#x1f4cc;举例 &#x1f4cc;题目描述 &#x1f4cc;解题思路 动态规划&#xff08;DP&#xff09; 问题&#xff0c;核心是 “前 i 种物品&#xff0c;每种物品最多可以使用x 次&#xff0c;组成总和…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...