客户拜访管理

1 什么是客户拜访

(1)客户:与公司有业务往来的

(2)用户:可以使用系统的人

2 用户和客户关系

(1)用户和客户之间是拜访的关系

(2)用户 和 客户 是 多对多关系

** 一个用户可以拜访多个客户,一个客户可以被多个用户拜访

(3)多对多建表:

- 创建第三张表,使用外键维护关系

3 多对多配置

(1)缺陷:第三张表只有两个字段(两个id值)

4 把多对多拆分成两个一对多实现
(1)用户、客户、拜访
(2)用户 和 拜访是一对多

(3)客户和拜访是一对多

配置用户和客户关系

(传统多对多中 是不需要创建中间实体类的  而是在配置文件中  相互表示 然后自动建中间表 这里因为要在中间表中增加额外的属性值,所以不使用之前的方式,创建中间实体类的同时,增加一些其他属性(拜访地址,拜访内容等等),同时使该实体类和user和customer互相表示,即把多对多 拆分为两个一对多,客户和拜访  一对多 , 用户和拜访  一对多  配置时  类比这客户、联系人的关系 一对多  进行配置)

第一步 创建三个实体类

第二步 实体类之间互相表示

(1)用户和拜访一对多

- 在用户实体类表示所有拜访记录,使用set集合

- 在拜访实体类里面表示所属用户,使用对象

(2)客户和拜访一对多

- 在客户实体类表示所有拜访记录,使用set集合

- 在拜访实体类表示所属客户,使用对象

第三步  配置映射关系

(1)拜访实体类visit  基本属性配置

(2)用户和拜访一对多

- 在用户映射文件中表示所有拜访记录,使用set标签

- 在拜访映射文件中表示所属用户

(3)客户和拜访一对多

- 客户映射文件中表示所有拜访

- 在拜访映射文件中表示所属客户

最后把映射配置文件在核心配置文件中做引入

代码实践截图:

拜访实体类visit

user实体类

customer实体类

visit.hbm.xml

user.hbm.xml

customer.hbm.xml

在核心配置文件中引入映射文件之后,启动服务器  然后自动建表

总结:在多对多映射配置中,推荐使用此方式,因为中间表中一般都不只是两个外键组成的,还拥有自己的私有属性,传统的简单多对多配置只有两个外键,而这里将多对多拆分成了两个一对多来进行配置,配置的时候可以类比多对一(联系人和客户)的思路进行配置

visit和user(多对一)   visit和customer(多对一)  

----------------------------------------------------------------------------------------

添加页面:

         <TABLE cellSpacing=0 cellPadding=0 width="98%" border=0>
<TBODY>
<TR>
<TD width=15 background=${pageContext.request.contextPath }/images/new_022.jpg><IMG
src="${pageContext.request.contextPath }/images/new_022.jpg" border=0></TD>
<TD vAlign=top width="100%" bgColor=#ffffff>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=0>
<TR>
<TD class=manageHead>当前位置:客户拜访管理 &gt; 添加拜访</TD>
</TR>
<TR>
<TD height=2></TD>
</TR>
</TABLE>
<TABLE cellSpacing=0 cellPadding=5 border=0>
<tr>
<td>客户:</td>
<!-- 应该用下拉列表显示所有的客户 而不是让用户输入 所有就需要在到达该页面之前在域对象中保存所有客户的记录 用于在这里显示 -->
<td colspan="3">
<!--<input type="text" name="custId" style="WIDTH: 180px"/>
-->
<select name="customer.cid">
<c:forEach items="${listCustomer}" var="customer">
<option value="${customer.cid}">${customer.custName}</option>
</c:forEach>
</select>
</td>
</tr> <tr>
<td>用户:</td>
<!-- 应该用下拉列表显示所有的客户 而不是让用户输入 所有就需要在到达该页面之前在域对象中保存所有客户的记录 用于在这里显示 -->
<td colspan="3">
<!--<input type="text" name="custId" style="WIDTH: 180px"/>
-->
<select name="user.id">
<c:forEach items="${listUser}" var="user">
<option value="${user.id}">${user.name}</option>
</c:forEach>
</select>
</td>
</tr> <TR>
<td>拜访地址:</td>
<td>
<INPUT class=textbox id=sChannel2
style="WIDTH: 180px" maxLength=50 name="vaddress">
</td>
<td>拜访内容:</td>
<td>
<INPUT class=textbox id=sChannel2
style="WIDTH: 180px" maxLength=50 name="vcontent">
</td>
</TR> <tr>
<td rowspan=2>
<INPUT class=button id=sButton2 type=submit
value="保存 " name=sButton2>
</td>
</tr>
</TABLE>

运行界面:

在action中使用模型驱动

点击提交数据库中就保存了一条记录:

操作和一对多都一样,只是配置关系时  需要把多对多 拆分为两个一对多

------------------------------------------------------------------------------------

抽取BaseDao

内容介绍
1 代码review

2 抽取basedao代码重构方式
(1)在dao层,做操作最基本crud操作
(2)做crud操作时候,调用hibernate模板的方法都一样
(3)不同的是参数不同,
比如操作客户,传递客户对象,比如操作联系人,传递联系人对象
(4)抽取basedao,减少dao层大量重复的代码

3 使用反射+泛型实现抽取
(1)反射得到类的.class文件内容
- 得到类Class方式
-- Class.forName(...)
-- 类名.class
-- 对象.getClass() ;
this.getClass() ;
-- getClass方法是Object类的方法

(2)泛型术语
List<User> :
- <> :读成 typeof
- List<User> :整体部分读成 参数化类型
- <User> : <>中的User,读成 实际类型参数

具体实现
1 创建basedao接口,使用泛型方式
在接口里面定义操作的方法curd方法

2 创建BaseDao接口实现类

3 在真正功能的接口,继承basedao接口

(1)把实现crud操作的方法去掉了

4 让真正功能的实现类继承BaseDao实现类
(1)在真正的实现类,不需要继承hibernateDaoSupport,因为baseDaoImpl类继承了hibernateDaoSupport  所以不需要重复继承,所以customerdaoimpl对象中仍然需要注入sessionfactory对象

5 上面操作实现基本功能,有两个查询没有实现(需要得到当前操作的对象才能实现,比如findAll查询的时候,需要传递查询的实体类名,而这些操作需要在父类basedaoimpl的构造函数中进行 )

(1)根据id查询,返回对象
(2)查询所有

6 当customerdaoimpl实现类的对象创建的时候,父类的构造执行,即先执行basedaoimpl的构造方法,在这个方法中需要得到操作类的名称,结束之后才会调用custoemrdaoimpl类的构造方法

(1)在BaseDaoImpl的构造方法中得到传递对象的class
(2)得到BaseDaoImpl泛型里面Customer的class

7 具体实现
(1)得到运行类的class

(2)得到customerdaoimpl父类内容

(3)得到父类的参数化类型

(4)获取实际类型参数

(5)代码实现
private Class clazzType;
public BaseDaoImpl() {
//1 获取当前运行对象的class
//比如运行customerDao实现类,得到customerDao实现类class
Class clazz = this.getClass();

//2 获取运行类的父类的参数化类型BaseDaoImpl<Customer>   现在customerdaoimpl的父类是basedaoimpl 因为该类继承了basedaoimpl
Type type = clazz.getGenericSuperclass();

//3 转换成子接口ParameterizedType
ParameterizedType ptype = (ParameterizedType) type;

//4 获取实际类型参数
//比如 Map<key,value>
Type[] types = ptype.getActualTypeArguments();

//5 把Type变成class
Class clazzParameter = (Class) types[0];
this.clazzType = clazzParameter;
}

注意hql语句中,from后面是需要空格的

----------------------------------------------

代码实现,以customer为例

这里只有customerdaoimpl类继承了basedaoimpl  代码的执行顺序是:在创建custoemrdoaimpl对象之前,因为该类继承了basedaoimpl  ,则,basedaoimpl就是customerdaoimpl的父类,那么在创建customerdaoimpl对象的时候,会先执行basedaoimpl

类的构造方法,然后才执行customerdaoimpl类的构造方法,可通过反射方式,在父类的构造方法中得到当前运行的类的名称

basedao接口:

 package org.dao;

 import java.util.List;

 public interface BaseDao<T> {
//修改
public void update(T t);
//添加
public void add(T t);
//删除
public void delete(T t);
//查找所有
public List<T> findAll();
//根据id值 进行查找 一条记录
public T findOne(int id);
}

basedaoimpl:

 package org.dao;

 import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List; import org.model.Customer;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport; public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T>{
//这里只抽取最基本的方法 即增删改查
private Class classType=null;
public BaseDaoImpl() {
super();
//得到当前运行类的Class
16 Class clazz=this.getClass();
17 //得到运行类 的父类参数化类型 例如当前运行类是customer CustomerDaoImpl extends BaseDaoImpl<Customer>
18 //那么basedaoimpl是customerdaoimpl的父类, 最终得到的结果就是BaseDaoImpl<Customer>
19 Type type=clazz.getGenericSuperclass();
20 //强转 子接口
21 ParameterizedType ptype=(ParameterizedType) type;
22 //使用子接口中的方法 得到实际类型参数 即<>中的内容
23 Type[] types=ptype.getActualTypeArguments();
24 Class clazzParameter=(Class) types[0];
this.classType=clazzParameter;
} public void add(T t) {
this.getHibernateTemplate().save(t);
} public void delete(T t) {
this.getHibernateTemplate().delete(t);
} public void update(T t) {
this.getHibernateTemplate().update(t);
} public List<T> findAll() {//classType
//"from Customer"
return (List<T>)this.getHibernateTemplate().find("from "+classType.getSimpleName());
}
public T findOne(int id) {
//(Customer.class,id)
return (T) this.getHibernateTemplate().get(classType, id);
} }

customerdao:

 package org.dao;

 import java.util.List;

 import org.model.Customer;
//接口之间也可以进行继承
public interface CustomerDao extends BaseDao<Customer>{//因为在basedao中已经存在了增删改查的方法,所以这里注释掉自己定义的方法
//T 真正实体类的名称 这里是customer
//void add(Customer customer); //List<Customer> findAll(); //Customer findOne(int id); //void delete(Customer c); //void update(Customer customer); int findCount(); List<Customer> findPage(int begin, int pageSize); List<Customer> findCondition(Customer customer); }

customerdaoimpl:

 package org.dao;

 import java.util.List;

 import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.model.Customer;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
//该类继承HibernateDaoSupport类 不需要注入HibernateTemplate对象了 只用在配置文件中注入sessionfactory对象
//而做basedao抽取的时候 需要继承baseDao的实现类 因为该实现类继承了hibernateDaoSupport 所以这里就
//不需c复继承了 但是在spring配置文件中 CustomerDaoImpl中还是需要注入sessionfactory类的
//因为该类虽然没有直接继承hibernateDaoSupport 但是因为basedaoimpl继承了 本类又继承了 basedaoimpl
//所以这个类就继承了hibernateDaoSupport
//另外在继承basedaoimpl时 需要明确泛型的类型 把泛型替换为具体的对象类型
public class CustomerDaoImpl extends BaseDaoImpl<Customer> implements CustomerDao { // public CustomerDaoImpl() {
// super();
// System.out.println("customerdaoimpl......构造函数执行");
// }
/*
* 因为在basedaoimpl中已经做了抽取 把基本的增删改查操作都做了实现 而且继承了basedaoimpl 直接在service中调用即可
*
*
//向t_customer表中保存一条记录
public void add(Customer customer) {
//得到模板对象 使用方法进行数据的保存
this.getHibernateTemplate().save(customer);
}
//查询t_customer表中的所有记录
public List<Customer> findAll() {
// TODO Auto-generated method stub
return (List<Customer>) this.getHibernateTemplate().find("from Customer");
}
//根据id 查询对象
public Customer findOne(int id) {
// TODO Auto-generated method stub
return this.getHibernateTemplate().get(Customer.class,id);
}
//删除对象
public void delete(Customer c) {
// TODO Auto-generated method stub
this.getHibernateTemplate().delete(c);
}
//修改操作
public void update(Customer customer) {
// TODO Auto-generated method stub
this.getHibernateTemplate().update(customer);
}
*/
//查询总的记录数
public int findCount() {
// TODO Auto-generated method stub
//使用模板中的find方法来完成查询总记录数 查询出来的不是一个个的对象的集合
//而是一个数值 但是该方法反回的的是一个list集合 所以仍然需要使用list集合进行接收
List<Object>list=(List<Object>) this.getHibernateTemplate().find("select count(*) from Customer");
//从list中把值得到
if(list!=null&&list.size()!=0){
//变成int类型
Object obj=list.get(0);
//直接变是不行的 需要先变成Long型(long的包装类) 在转为int性
Long lobj=(Long) obj;
int count=lobj.intValue();
return count;
}
return 0;
}
//分页操作
public List<Customer> findPage(int begin, int pageSize) {
// TODO Auto-generated method stub
//创建离线DetachedCriteria对象 指明是对那个实体类进行操作
DetachedCriteria criteria=DetachedCriteria.forClass(Customer.class);
//第一个参数是离线对象 第二个 :起始位置 第三个 每页的大小(记录数)
List<Customer> list=(List<Customer>) this.getHibernateTemplate().findByCriteria(criteria,begin,pageSize);
return list;
}
//条件查询 输入的内容不为空
public List<Customer> findCondition(Customer customer) {
// TODO Auto-generated method stub
//第一种方式:
// SessionFactory sessionFactory=this.getHibernateTemplate().getSessionFactory();//得到sessionfactory对象
// Session session=sessionFactory.getCurrentSession();//得到session 这里的session对象和web阶段的HttpSession不是同一个概念
// Query query=session.createQuery("from Customer where custName like ?");
// query.setParameter(0,customer.getCustName());
// List<Customer> list=query.list(); //第二种方式: 单一条件查询的时候 这种比较简单 但是多条件查询的时候 使用第三中方式最简单
// List<Customer> list=(List<Customer>) this.getHibernateTemplate().find("from Customer where custName like ?","%"+customer.getCustName()+"%"); //第三种方式:
//创建离线对象,表示对那个实体类进行操作
DetachedCriteria criteria=DetachedCriteria.forClass(Customer.class);
//设置对实体类中的那个属性进行条件查询 如果对多个字段进行条件查询 那么只需要调用add方法即可
criteria.add(Restrictions.like("custName","%"+customer.getCustName()+"%"));
//调用模板中的方法 得到集合
List<Customer> list=(List<Customer>) this.getHibernateTemplate().findByCriteria(criteria);
return list;
} }

如果该实现类不注入sessionfactory,那么就会报错:

service中部分代码:因为继承了basedaoimpl 所以这里调用的方法  都是basedaoimpl中的方法

 // 添加客户
public void add(Customer customer) {
// TODO Auto-generated method stub
customerDao.add(customer);
} // 客户列表
public List<Customer> findAll() {
// TODO Auto-generated method stub
return customerDao.findAll();
} // 根据id查询对象
public Customer findOne(int id) {
return customerDao.findOne(id);
} // 删除对象
public void delete(Customer c) {
// TODO Auto-generated method stub
customerDao.delete(c);
} // 修改操作
public void update(Customer customer) {
// TODO Auto-generated method stub
customerDao.update(customer);
}

这样统一的功能就由basedao进行实现,继承basedaoimpl类,就可以使用基本的增删改查操作,从而减少了大量的代码

--------------------------------

当customerdaoimpl和linkmandaoimpl两个实现类继承basedaoimpl的时候,服务器的启动截图:

说明创建对象之前会先调用父类中的构造方法,而在核心配置文件中是先引入的customer.xml文件,后引入的linkman.xml文件,所以加载配置文件的顺序就是先customer.xml后linkman.xml,所以在服务器启动的时候 先创建customer对象,后创建linkman对象,而创建这两个对象之前都会先调用其父类的构造方法。

-----------------------------------------------------------------------------------

多条件组合查询
第一种 写底层hibernate代码实现

第二种 使用hibernate模板里面find方法实现

- 写hql语句实现
//多条件组合查询
@SuppressWarnings("all")
public List<Customer> findMoreCondition(Customer customer) {
//使用hibernate模板里面find方法实现
//拼接hql语句
String hql = "from Customer where 1=1 ";
//创建list集合,如果值不为空,把值设置到list里面
List<Object> p = new ArrayList<Object>();
//判断条件值是否为空,如果不为空拼接hql语句
if(customer.getCustName()!=null && !"".equals(customer.getCustName())) {
//拼接hql
hql += " and custName=?";
//把值设置到list里面
p.add(customer.getCustName());
}
if(customer.getCustLevel()!=null && !"".equals(customer.getCustLevel())) {
hql += " and custLevel=?";
p.add(customer.getCustLevel());
}
if(customer.getCustSource()!=null && !"".equals(customer.getCustSource())) {
hql += " and custSource=?";
p.add(customer.getCustSource());
}
// System.out.println("hql: "+hql);
// System.out.println("list: "+p);
return (List<Customer>) this.getHibernateTemplate().find(hql, p.toArray());
}

第三种 使用离线对象和hibernate模板里面方法

第二种方式代码实现:

表单提交之后,把数据提交给action,因为action中使用了模型驱动,所以可以得到数据,如果没有输入任何东西,那么action中封装的模型对象中的数据就是为null,但是这不影响数据的检索,

action中直接调用service中的方法,service调用dao中的方法

下面是daoimpl中的方法:

     //多条件查询
public List<Customer> moreCondition(Customer customer) {
List<Object> p=new ArrayList<Object>();
String hql="from Customer where 1=1";
//下面进行验证是否为空 并拼接hql字符串
if(customer.getCustName()!=null&&!"".equals(customer.getCustName())){
hql=hql+" and custName=?";
p.add(customer.getCustName());//如果值不为空 就把该值设置进list集合中
}
if(customer.getCustLevel()!=null&&!"".equals(customer.getCustLevel())){
hql += " and custLevel=?";
p.add(customer.getCustLevel());
}
if(customer.getCustSource()!=null&&!"".equals(customer.getCustSource())){
hql += " and custSource=?";
p.add(customer.getCustSource());
}
//第二个参数是一个可变参数(就是一种数组的形式) 需要把list集合变为数组
return (List<Customer>) this.getHibernateTemplate().find(hql,p.toArray());
}

在这方法中,我们采用的是字符串拼接的方式来构造hql语句,因为用户可能什么也没用输入,所以就需要判断是否为空,如果为空,那么if语句就都没有执行,所以hql语句就变为:

String hql="from Customer where 1=1";
当然是查询所有了
所以通过字符串拼接的方式,就能够完成多条件的查询操作 第三种方式代码实现:
daoimpl代码:
     //多条件查询
public List<Customer> moreCondition(Customer customer) {
//第二种方式:使用离线对象
DetachedCriteria c=DetachedCriteria.forClass(Customer.class);//DetachedCriteria离线查询
//仍然需要验证是否为空 如果不为空 就像离线对象中插入限制条件 并且设置值
if(customer.getCustName()!=null&&!"".equals(customer.getCustName())){
c.add(Restrictions.eq("custName",customer.getCustName()));
}
if(customer.getCustLevel()!=null&&!"".equals(customer.getCustLevel())){
c.add(Restrictions.eq("custLevel",customer.getCustLevel()));
}
if(customer.getCustSource()!=null&&!"".equals(customer.getCustSource())){
c.add(Restrictions.eq("custSource",customer.getCustSource()));
}
//调用方法 执行查询
return (List<Customer>) this.getHibernateTemplate().findByCriteria(c);
}
DetachedCriteria--离线查询
Restrictions--条件限制

SSH框架整合截图(二)的更多相关文章

  1. SSH框架整合截图总结(一)

    分页相关属性 --------------------------------------------------------------- 分页思路表单提交(只需传递当前页的值) ->acti ...

  2. SSH框架整合截图总结(三)

    联系人信息查询1 点击 联系人信息查询 超链接时候,到查询页面 (1)在查询页面中,选择客户,根据客户进行查询 下拉表框显示所有客户  可以根据所属的客户进行联系人查询  2 在查询页面中,输入值,提 ...

  3. SSH框架整合

    SSH框架整合 一.原理图 action:(struts2) 1.获取表单的数据 2.表单的验证,例如非空验证,email验证等 3.调用service,并把数据传递给service Service: ...

  4. dwr与ssh框架整合教程

    (1)dwr与ssh框架整合教程dwr框架介绍. DWR(Direct Web Remoting)是一个用于改善web页面与Java类交互的远程服务器端Ajax开源框架,可以帮助开 发人员开发包含AJ ...

  5. ssh框架整合之登录以及增删改查

    1.首先阐述一下我用得开发工具,myeclipse2017+oracle,所以我的基本配置步骤可能不一样,下面我用几张图来详解我的开发步骤. ---1先配置structs (Target 选择apac ...

  6. Spring+Hibernate+Struts(SSH)框架整合

    SSH框架整合 前言:有人说,现在还是流行主流框架,SSM都出来很久了,更不要说SSH.我不以为然.现在许多公司所用的老项目还是ssh,如果改成流行框架,需要成本.比如金融IT这一块,数据库dao层还 ...

  7. MVC+Spring.NET+NHibernate .NET SSH框架整合 C# 委托异步 和 async /await 两种实现的异步 如何消除点击按钮时周围出现的白线? Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法

    MVC+Spring.NET+NHibernate .NET SSH框架整合   在JAVA中,SSH框架可谓是无人不晓,就和.NET中的MVC框架一样普及.作为一个初学者,可以感受到.NET出了MV ...

  8. SSH框架整合过程总结

    ---------------------siwuxie095                                 SSH 框架整合过程总结         (一)导入相关 jar 包(共 ...

  9. J2EE进阶(十)SSH框架整合常见问题汇总(一)

    SSH框架整合常见问题汇总(一) 前言 以下所列问题具有针对性,但是遇到同类型问题时均可按照此思路进行解决. HTTP Status 404 - No result defined for actio ...

随机推荐

  1. pomelo研究笔记-RPC服务端

    POMELO 採用多进程的架构能够非常好的实现游戏server(进程)的扩展性,达到支撑较多在线用户.减少server压力等要求. 进程间通信採用RPC的形式来完毕,pomelo的RPC实现的相当静止 ...

  2. 黑马day07 登录注冊案例(一)

    简单介绍:依据三层架构的思想设计本案例. 1.搭建好开发环境 准备好须要的包和模拟数据库配置文件users.xml -->cn.itheima.dao -->cn.itheima.serv ...

  3. [深入理解Android卷一全文-第七章]深入理解Audio系统

    由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. ...

  4. Java异常的捕获与处理

    Java提供了try(尝试).catch(捕捉).finally(最终)这三个关键字来处理异常.在处理各种异常时,需要用到对应的异常类,指的是由程序抛出的对象所属的类. 一.异常处理的使用 由于fin ...

  5. ASP.NET MVC2 Web项目中公用类库的问题

    ASP.NET WEB窗体 网站中,加入公用类文件的话,系统会很自动并殷勤的问你,说要不要把它存放在文件夹 App_Code 里.一旦加入,全站都可以很方便地加以使用,一点问题没有. 这种习以为常的方 ...

  6. 修改android手机文件权限

    修改android手机文件权限 默认情况下,一个应用肯定是读取不了另外一个应用的数据的,因为权限不够.但是我们一定要读,怎么办? 修改我们要读取文件的权限. Android是基于Linux的,所以修改 ...

  7. spark rdd median 中位数求解

    lookup(key) Return the list of values in the RDD for key key. This operation is done efficiently if ...

  8. 杂项: EasyUI | jQuery EasyUI

    ylbtech-杂项-JS: EasyUI | jQuery EasyUI jQuery EasyUI是一组基于jQuery的UI插件集合体,而jQuery EasyUI的目标就是帮助web开发者更轻 ...

  9. JDBC基础02

    今日知识 1. sql注入问题2. jdbc批处理3. 事务 SQL注入问题解决 1.什么是sql注入. * 用户通过相关的特殊关键字sql语句非法访问数据库 *例如: Xxx(' or '1'='1 ...

  10. BZOJ 1196 二分+Kruskal

    思路: 二分答案 判一下能不能加 //By SirisuRen #include <cstdio> #include <cstring> #include <algori ...