Mybatis 一对一、一对多、多对多关联之级联添加
示例项目:MIPO_CRM
一、一对一关联
示例:订单与销售机会
描述:在业务员与客户的联系人的联系记录中可以生成一条销售机会,而此条销售机会可生成一条订单,两者呈一对一关联。
1.表设计
opportunity(销售机会表)
orders(订单表)
2.pojo
Opportunity
/** * 销售机会机会 * @author Administrator * */ public class Opportunity implements Serializable{ private int opid; private Float allprice;//所有商品的购买总价 private int allcount;//所有商品的购买数量 private String odate;//下单时间 private User user;//业务员 private Linkman linkman;//联系人 }
Orders
/** * 订单 * @author Administrator * */ public class Orders implements Serializable { private String oid;//订单id private Opportunity opportunity;//销售机会 (订单与销售机会呈一对一关联) //... }
3.下面看一下添加订单的代码是如何级联添加销售机会的:
@RequestMapping(value = "addOrder", method = RequestMethod.POST) public String addOrder(Map<String, Object> maps, Orders orders) { // 通过opid获取销售机会 Opportunity opportunity = opportunityService.findOppById(orders .getOpportunity().getOpid()); // ...
// 生成订单oid String oid = ""; for (int i = 0; i < 5; i++) { oid += (int) (Math.random() * 10); } oid += System.currentTimeMillis() + "";// 创建订单 Orders ord = new Orders(oid, opportunity, opportunity.getLinkman(), opportunity.getUser(), orders.getBdate(), orders.getFdate(), opportunity.getAllprice(), 1, 1, orders.getRemark()); //...
// 级联添加订单表、订单商品表 ordersService.addOrder(ord, ordergoods); return "redirect:/toPage/listOrdersPage"; }
从上面代码可看出,在添加订单时先获取销售机会,然后在创建订单时将销售机会加进去就好了。
4.最后看一下Mybatis添加订单时的SQL
添加订单(orders.xml)
<!-- 添加订单表 --> <insert id="insert" parameterType="Orders"> insert into Orders(oid,opid,bdate,lid,uid,fdate,ysprice,flag,statues,remark) values(#{oid},#{opportunity.opid},#{bdate},#{linkman.lid},#{user.uid},#{fdate},#{ysprice},#{flag},#{statues},#{remark}) </insert>
从上面的代码可以看出,往数据库添加订单时再把销售机会的opid(opportunity.opid)拿出来添加到订单表就可以了。
下面是数据库的数据:
说明:1.这里的一对一比较特殊一点,因为一般情况下,一对一都有一个主对象和一个附属对象,附属对象的创建依赖于主对象的创建,附属对象的主键id也是外键,它与主对象的主键id相等,因此两个对象所映射的表中不需要再创建一个列来进行关联(如账户与员工);但在这里由于订单表的主键oid不是“1,2,3..”这样的数字,在添加时无法将销售机会的主键值作为自己的主键值,因此在键表时设立一列opid来与销售机会表进行关联。2.由于添加销售机会与添加订单在业务逻辑上呈明显的先后顺序,因此没有在添加销售机会时级联添加订单或在添加订单时级联添加销售机会。
二、一对多关联
示例:订单与订单商品
描述:订单描述了客户购买商品的时间,合同截止时间,购买商品的总价、业务员、购买商品的联系人等信息,但无法描述客户具体买了哪些商品,每件商品又买了多少,购买单价是多少,该商品的利润又是多少,所以此项目创建了订单商品表以描述这些信息。因为客户在签约时生产一张订单,而该客户可能购买多件商品,所以此订单有可能对应多个订单商品,订单与订单商品呈一对多关联。
1.表设计
orders(订单表)
ordergood(订单商品表)
从订单商品表可以看出此表同过gid、oid列关联了商品表、订单表。
2.POJO
Orders
/** * 订单 * @author Administrator * */ public class Orders implements Serializable { private String oid;//订单id private Opportunity opportunity;//销售机会 (订单与销售机会呈一对一关联) private Linkman linkman;//联系人 (订单与联系人呈多对一关联) private User user;//业务员 (订单与业务员呈多对一关联) private Date bdate; //开单日期 private Date fdate;//合同到期时间 private Float ysprice;//应收金额 private int statues;//审核状态 private Integer flag;//订单状态 private String remark;//备注 private Integer uids;//订单审核人 }
Ordergood
/** * 订单商品 * @author Administrator * */ public class Ordergood implements Serializable { private Orders orders;//订单 (订单商品与订单呈多对一关联) private Goods goods;//商品 (订单商品与商品呈多对一关联)
private Integer count;//同件商品的数量 private Float price;//同件商品的购买单价 private Float allprice;//同件商品的购买总价 private Float profit;//同件商品的单件利润 }
3.下面看一下在添加订单时是如何级联添加订单商品的:
@RequestMapping(value = "addOrder", method = RequestMethod.POST) public String addOrder(Map<String, Object> maps, Orders orders) { // 1.通过opid查询销售机会表对应的内容 Opportunity opportunity = opportunityService.findOppById(orders .getOpportunity().getOpid()); // 2,通过opid查询商品机会表的内容 List<Goodopp> goodopps = goodoppService.listGoodopp1(orders .getOpportunity().getOpid()); // 生成订单oid String oid = ""; for (int i = 0; i < 5; i++) { oid += (int) (Math.random() * 10); } oid += System.currentTimeMillis() + ""; System.out.println(oid+"oid"); /** * 获取订单的信息 * 1.生成的“订单”中的联系人、业务员、所有商品的总价从对应的“销售机会表”中获取。 * 2.生成的订单的初始状态:“审核状态”为1,表示待审核;“订单状态”为1,表示可取消订单; * 审核人为空,表示待审核人审核。 */ Orders ord = new Orders(oid, opportunity, opportunity.getLinkman(), opportunity.getUser(), orders.getBdate(), orders.getFdate(), opportunity.getAllprice(), 1, 1, orders.getRemark()); /** * 获取订单商品的信息 * 生成的“订单商品”中的同件商品的购买价格、购买数量、商品信息从对应的“商品销售机会表”中获取。 */ List<Ordergood> ordergoods = new ArrayList<Ordergood>(); for (Goodopp goodopp : goodopps) { Ordergood ordergood = new Ordergood(); ordergood.setOrders(ord);//设置订单 ordergood.setGoods(goodopp.getGoods());//设置商品 ordergood.setPrice(goodopp.getPrice());//设置同件商品的购买价格 ordergood.setCount(goodopp.getCount());//设置同件商品的购买数量 ordergood.setAllprice(goodopp.getPrice() * goodopp.getCount());//设置同件商品的购买总价 Goods goods = goodsService.findGoodsById(goodopp.getGoods() .getGid());// 获取商品销售机会对应的商品 Float outprice = goodopp.getPrice();//获取同件商品的购买单价 Float inprice = goods.getCostprice();//获取同件商品的成本价 ordergood.setProfit(outprice - inprice);//设置同件商品的单件利润 ordergoods.add(ordergood); } // 级联添加订单表、订单商品表 ordersService.addOrder(ord, ordergoods); return "redirect:/toPage/listOrdersPage"; }
4.下面是上面代码中ordersService.addOrder(ord, ordergoods);所调用服务层的代码:
/** * 生成订单,并添加订单商品表 * @param orders * @param ordergoods */ public void addOrder(Orders orders,List<Ordergood> ordergoods){ ordersDao.addOrder(orders);//添加订单 ordergoodDao.addOrdergood(ordergoods);//添加订单商品表 }
5.现在省略这两个方法所调用的模型层的方法,看下各自在Mybatis映射文件中的sql
a.添加订单的sql(orders.xml)
<!-- 添加订单表 --> <insert id="insert" parameterType="Orders"> insert into Orders(oid,opid,bdate,lid,uid,fdate,ysprice,flag,statues,remark) values(#{oid},#{opportunity.opid},#{bdate},#{linkman.lid},#{user.uid},#{fdate},#{ysprice},#{flag},#{statues},#{remark}) </insert>
b.添加订单商品的sql(ordergood.xml)
<!--添加订单商品表。因为一次添加的订单商品可能有多条数据,因此这里进行批量添加。--> <insert id="addOrdergood" parameterType="java.util.List"><!-- parameterType="java.util.List"可以省略,Mybatis会自动判断参数类型。 --> insert into ordergood(oid,gid,count,price,allprice,profit) values <foreach collection="list" item="og" separator=","><!-- separator="," 不可以省略;item="og"是集合中每一个元素进行迭代时的别名,可以随便取。 --> (#{og.orders.oid},#{og.goods.gid},#{og.count},#{og.price},#{og.allprice},#{og.profit}) </foreach> </insert>
小结:Mybatis作为模型框架与Hibernate在级联添加一对多这样的关系对象的区别:
1.在定义POJO上:
从POJO的定义就可以看出,使用Mybatis作为模型层与Hibernate在定义POJO时的区别:如果是Hibernate则需要在Orders类中定义一个装Ordergood的集合,如:
private Set<Ordergood> = new HashSet<Ordergood>();
而Mybayis则只是在多的一方的POJO类中(如Ordergood)定义一的一方(Orders)就可以了。
2.在映射文件上:
Hibernate需要在映射文件中定义对象之间的关系;而Mybatis不需要。
3.在添加方式上:
Hibernate只需调用Set方法将关联的一方实例进行赋值,然后Hibernate在添加本对象至数据库时会自动地级联添加其关联对象;而Mybatis则需要手工添加本对象及关联对象。
三、多对多关联
示例:角色与权限
描述:一个角色可对应多个权限,一个权限也可对应多个角色,因此两种之间呈多对多关联。
1.表设计
role(角色表)
function(权限表)
rolefun(角色权限表)
2.POJO
Role
/** * 角色 * @author Administrator * */ public class Role implements Serializable { private Integer rid; private String rname; private String rdesc; }
Function
/** * 权限 * @author Administrator * */ public class Function implements Serializable { private Integer fid; private String fname; private String method; private String submitway;//提交方式 private Integer parentid; private List<Function> childFun;//该父权限下的子权限 }
RoleFun
/** * 角色权限 * @author Administrator * */ public class RoleFun implements Serializable { private Integer roleid; private Integer funid; }
3.下面看一下添加角色时是如何级联添加权限的
@RequestMapping(value="operRole",method=RequestMethod.POST) public String addRole(HttpServletRequest request,Role role){ //获取权限id String fids[]=request.getParameterValues("fids"); //添加角色表并级联角色权限表 roleService.addRoleFuns(role,fids ); return "redirect:toPage/listRole"; }
4.下面看一下上面方法中roleService.addRoleFuns(role,fids );调用的服务层的代码
public void addRoleFuns(Role role,String[] fids){ //添加角色表 roleDao.addRole(role); //定义角色权限 List<RoleFun> roleFuns=new ArrayList<RoleFun>(); //迭代添加角色权限 for (int i = 0; i < fids.length; i++) { roleFuns.add(new RoleFun(role.getRid(), Integer.parseInt(fids[i]))); } //添加角色权限表 roleFunDao.addRoleFun(roleFuns); }
5.现在省略其中调用的模型层的代码,看一下Mybatis中相应的sql
a.添加角色(role.xml)
<!-- 添加角色 --> <insert id="insert" parameterType="Role" useGeneratedKeys="true" keyProperty="rid"> insert into role(rname,rdesc) values(#{rname},#{rdesc}) </insert>
b.添加角色权限(rolefun.xml)
<!-- 添加角色权限 --> <insert id="insert" > insert into rolefun(roleid,funid) values <foreach collection="list" item="rolefun" separator=","> (#{rolefun.roleid},#{rolefun.funid}) </foreach> </insert>
小结:Mybatis作为模型框架与Hibernate在级联添加多对多这样的关系对象的区别:
1.在定义POJO上:
a.Hibernate需要关联的双方在自己的类中用集合定义关联对象的关系,如在定义Role(角色)类时,就要定义:private Set<Function> = new HashSet<Function>();
在定义Function(权限)类时,就要定义:private Set<Role> = new HashSet<Role>();Mybatis不需要。
b.Hibernate不需要定义中间类(如RoleFun类);Mybatis需要。
2.在映射文件上:
Hibernate需要在各自的映射文件中定义与关联对象的关系;而Mybatis则不需要。
3.在添加方式上:
Hibernate调用Set方法将关联实例赋值,然后在添加时Hibernate会自动地级联添加关联对象;Mybatis是手工添加本对象及关联对象。
后记:Mybatis与Hibernate在级联查询、修改、删除对象的方式也是不同的,本博客不再赘述,以后再写。