原理
其实延迟加载的实现原理很简单,其实就是对方法进行拦截
假设现在有两个表格kf
和wallpaper
,这两个表存在关联关系(这里是我强行绑定的),通过注解@Collection
来进行关系的标注
kf 表格对应的java对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| @Table(name = "kf") public class Kf {
@Id(name = "kf_id") @Column(name = "kf_id") private int id; @Column(name = "kf_name") private String kfName; @Column(name = "kf_account") private String kfAccount; @Column(name = "kf_qcard") private String kfQcard; @Collection(sub = WallPaper.class) private List<WallPaper> wallPapers;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getKfName() { return kfName; }
public void setKfName(String kfName) { this.kfName = kfName; }
public String getKfAccount() { return kfAccount; }
public void setKfAccount(String kfAccount) { this.kfAccount = kfAccount; }
public String getKfQcard() { return kfQcard; }
public void setKfQcard(String kfQcard) { this.kfQcard = kfQcard; }
public List<WallPaper> getWallPapers() { return wallPapers; }
public void setWallPapers(List<WallPaper> wallPapers) { this.wallPapers = wallPapers; }
@Override public String toString() { return "Kf{" + "id=" + id + ", kfName='" + kfName + '\'' + ", kfAccount='" + kfAccount + '\'' + ", kfQcard='" + kfQcard + '\'' + ", wallPapers=" + wallPapers + '}'; } }
|
wallpaper 表格对应的java对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Table(name = "wallpaper") public class WallPaper {
@Id(name = "id") @Column(name = "id") private int id; @Column(name = "back_image_url") private String backImageUrl;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getBackImageUrl() { return backImageUrl; }
public void setBackImageUrl(String backImageUrl) { this.backImageUrl = backImageUrl; }
@Override public String toString() { return "WallPaper{" + "id=" + id + ", backImageUrl='" + backImageUrl + '\'' + '}'; } }
|
然后想在查询kf的时候把对应的wallpaper查询出来,这个时候,就需要做一些小小的操作,我们将数据库查询出来的kf
表格的数据,不再直接简单的分装成Kf
对象,而是采用CGLib字节码库,对Kf
对象进行代理,生成一个Kf
的代理对象返回给用户,这样,当用户需要执行kf
的getWallPapers
时,会被拦截,这个时候我们就可以在这里继续加载被关联表格的数据,然后将查询出来的数据装入obj中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @SuppressWarnings("unchecked") public T newInstance(Map<String, Object> values) { final Map<String, Field> columnFieldMapper = this.columnFieldMapper; T t = null; try { if (hasCascadeRelations()) { t = (T) CglibProxy.getProxy(tableCls); for (Map.Entry<Field, CascadeRelations> entry : cascadeMapper.entrySet()) { Field field = entry.getKey(); field.setAccessible(true); field.set(t, CglibProxy.getProxy(field.getType())); } } else { t = tableCls.newInstance(); } for (Map.Entry<String, Object> entry : values.entrySet()) { Field field = columnFieldMapper.get(entry.getKey()); field.setAccessible(true); field.set(t, entry.getValue()); } } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } return t; }
|
延迟加载实现
对方法进行拦截,当拦截到get方法时,通过方法获取对应的Field信息,对Field进行判断,如果Field是另一个Table的话,就进行数据查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @SuppressWarnings("unchecked") @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { Object invokeResult = proxy.invokeSuper(obj, args); String methodName = method.getName(); if (invokeResult == null) { return null; } else if (methodName.startsWith("get")) { Class<?> invokeResultClass = invokeResult.getClass(); Class<?> superClass = invokeResultClass.getSuperclass(); if (invokeResultClass.equals(superClass)) { return invokeResult; } Field field = getFieldBy(method); field.setAccessible(true); CascadeRelations cascadeRelations = CascadeRelations.type(field); if (cascadeRelations == null) { return invokeResult; } Class<?> relationClass; if (cascadeRelations == CascadeRelations.ASSOCIATION) { relationClass = field.getAnnotation(Association.class).sub(); } else { relationClass = field.getAnnotation(Collection.class).sub(); } Sql sql = new Select().getSql(relationClass); List list = SqlSessionFactory.getInstance().openSession().getJdbcTemplate().selectByRelation(sql, Map.class); field.set(obj, list); return list; } return invokeResult; }
|
不足
目前该代码的实现很粗糙,其实只是探究了MyBatis延迟加载的原理而已,还有很多数据库细节还没有去完整的考虑