1. 前面分析了guzz加载guzz.xml的过程,今天继续解析guzz加载数据库方言,初始化数据类型的过程
2. 首先在GuzzContextImpl的initFromMainConfig方法中找到下面的代码:
//加载dialect初始化数据类型 Map ds = builder.getConfiguredDialect() ; if(ds == null){ log.warn("dialect(s) not found.") ; }else{ this.dialects = ds ; }这里就是guzz在加载dialect初始化数据类型,主要的实现在GuzzConfigFileBuilder的getConfiguredDialect方法中,因为整个guzz.xml的文件都是加载到了GuzzConfigFileBuilder中,进入到getConfiguredDialect()方法中
public Map getConfiguredDialect(){ List ls = this.rootDoc.selectNodes("dialect") ; if(ls == null) return null ; if(ls.isEmpty()) return null ; HashMap ds = new HashMap() ; for(int i = 0 ; i < ls.size() ; i++){ Element e = (Element) ls.get(i) ; String d_cls = e.attributeValue("class") ; String d_name = e.attributeValue("name") ; if(StringUtil.isEmpty(d_name)){ d_name = "default" ; } Dialect dialect = (Dialect) BeanCreator.newBeanInstance(d_cls) ; //注册用户自定义的类型 List types = e.selectNodes("type") ; for(int j = 0 ; j < types.size() ; j++){ Element t = (Element) types.get(j) ; String typeName = t.attributeValue("name") ; String className = t.attributeValue("class") ; Class cls = ClassUtil.getClass(className) ; Assert.assertTrue(sqlDataType.class.isAssignableFrom(cls),"user-defined data type must be a instance of type:" + sqlDataType.class.getName()) ; dialect.registerUserDefinedTypes(typeName,cls) ; } ds.put(d_name,dialect) ; } return ds ; }
首先查询了文档中所有的dialect节点,然后查询了每一个节点的属性(calss,name),若没有配置name属性,那么就默认是default,这里和帮助文档中的一致,根据配置的class,通过BeanCreator创建了一个实例,具体是什么对象,要看guzz.xml中的配置,我这里的配置是:
<dialect class="org.guzz.dialect.MysqL5Dialect"></dialect>所有创建的实例就是MysqL5Dialect,由于guzz.xml支持多个数据库,所以方言肯定也是不同的,Guzz的方言类结构图如下:
从类结构图可以看出来guzz支持的数据库有哪些,当然我们自己根据需要也可以自己定义需要的数据库方言,只要实现AbstractDialect这个抽象类就可以了,因为我使用的是MysqL,所以也就只看了MysqL5Dialect,打开看类,只有一个主要的方法,就是对MysqL的limit的出来,每一中数据库的分页查询方式都有不同的实现。接着就是查看父类进入到AbstractDialect类,有3个重要的方法regSystemTypes,getDataType,registerUserDefinedTypes
3. 首先看regSystemTypes,部分代码:
protected void regSystemTypes(){ sqlTypes.put("int",IntegersqlDataType.class) ; sqlTypes.put("Integer",IntegerObjectsqlDataType.class) ; sqlTypes.put(Integer.class.getName(),IntegerObjectsqlDataType.class) ; sqlTypes.put("string",StringsqlDataType.class) ; sqlTypes.put("varchar",StringsqlDataType.class) ; sqlTypes.put("nvarchar",StringsqlDataType.class) ; .... }一看就明白,把java对应的数据类型和数据库中的数据类型都写了一个对应的出来类,放入到了一个map对象中。getDataType方法看名字也知道了是获取数据列的类型处理类也就是sqlDataType,类结构图:
4. 然后我们回到getConfiguredDialect,继续看,在这个方法中我们看到了在找了dialect节点后,又在dialect节点下面找子节点type,若是有配有type的话就解析后调用registerUserDefinedTypes方法,这个方法是注册用户自定义的数据类型,这点在帮助文档中没有提到,但实际上Guzz是有这个功能的。
出来完了所有的dialect节点后返回存有dialect的一个map,这个map中村了所有的dialect,每一个dialect对象中又存有所有的数据类型(sqlDataType)
5. 到此为止,guzz 完成了加载dialect初始化数据类型。
6. 刚才说到guzz可以自定义数据类型,出来特定类型的对象,但是guzz帮助文档没有提到,我们写一个简单的例子测试这个功能:
6.1 定义两个实体类员工类 Emp 和部门类Depart
package com.hqhp.maven.model; import javax.persistence.Column; import org.guzz.annotations.Table; /** * 员工 * @author 确实比较男 */ @javax.persistence.Entity @org.guzz.annotations.Entity(businessName = "emp") @Table(name = "tb_emp") public class Emp extends BaseModel { private static final long serialVersionUID = -3759132536120038862L; private String name; private int age; @Column(name = "departid") private Depart depart; //getter setter 省略 @Override public String toString() { return "Emp [name=" + name + ",age=" + age + ",depart=" + depart + "]"; } }
package com.hqhp.maven.model; import org.guzz.annotations.Table; /** * 部门 * @author 确实比较男 */ @javax.persistence.Entity @org.guzz.annotations.Entity(businessName = "depart") @Table(name = "tb_depart") public class Depart extends BaseModel { private static final long serialVersionUID = -2394287758178521691L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Depart [name=" + name + "]"; } }
6.2 guzz.xml配置文件如下:
<guzz-configs> <dialect class="org.guzz.dialect.MysqL5Dialect"> <type name="com.hqhp.maven.model.Depart" class="org.guzz.orm.type.DepartsqlDataType" /> </dialect> <tran locator="spring"> <dbgroup name="default" masterDBConfigName="masterDB" /> </tran> <!-- 类似于MysqL数据库的配置文件 --> <config-server> <server class="org.guzz.config.LocalFileConfigServer"> <param name="resource" value="guzz_app.properties" /> </server> </config-server> <business-scan resources="classpath*:com/hqhp/maven/model/*.class" /> </guzz-configs>根据源码的分析,我们的dialect节点可以这样写。
6.3 接下来我们就开始写org.guzz.orm.type.DepartsqlDataType,也就是自定义sqlDataType,仿照到guzz的写,代码如下:
package org.guzz.orm.type; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.sqlException; import org.guzz.exception.DataTypeException; import com.hqhp.maven.dao.UserDao; import com.hqhp.maven.model.Depart; import com.hqhp.maven.utils.SpringUtil; /** * @author 确实比较男 */ public class DepartsqlDataType implements sqlDataType { @Override public void setNullToValue(Object nullValue) { System.out.println("setNullToValue..............."); if (nullValue != null) { throw new DataTypeException("null value unsupported. nullValue is:" + nullValue); } } @Override public Object getsqlValue(ResultSet rs,String colName) throws sqlException { System.out.println("getsqlValue...........colName...."); String departid = rs.getString(colName); System.out.println("=====:" + departid); UserDao userDao = (UserDao) SpringUtil.getBean("userDao"); Depart depart = userDao.queryDepart(departid); System.out.println(depart.getId() + " " + depart.getName()); return depart; } @Override public Object getsqlValue(ResultSet rs,int colIndex) throws sqlException { System.out.println("getsqlValue..........colIndex....."); String departid = rs.getString(colIndex); UserDao userDao = (UserDao) SpringUtil.getBean("userDao"); Depart depart = userDao.queryDepart(departid); System.out.println(depart.getId() + " " + depart.getName()); return depart; } @Override public void setsqlValue(PreparedStatement pstm,int parameterIndex,Object value) throws sqlException { System.out.println("setsqlValue..................."); System.out.println(value); Depart depart = (Depart) value; System.out.println(depart.getId()); pstm.setString(parameterIndex,depart.getId()); } @Override public Class getDataType() { System.out.println("getDataType..............."); return Depart.class; } @Override public Object getFromString(String value) { System.out.println("getFromString..............."); return null; } }主要是这两个方法,在保存和查询的时候会调用。
public boolean insertEmp(Emp emp) { TransactionManager tm = guzzDao.getTransactionManager(); WriteTranSession session = tm.openRWTran(true); try { return session.insert(emp) == null ? false : true; } finally { session.close(); } } public Emp queryEmp(Serializable id) { TransactionManager tm = guzzDao.getTransactionManager(); ReadonlyTranSession session = tm.openDelayReadTran(); try { return (Emp) session.findObjectByPK(Emp.class,id); } finally { session.close(); } }
向数据库中插入一个emp对象,若是把depart的id存入了数据库那么说明保存对象正确;查询一个emp对象,若是取出的emp对象的depart对象不为空那么查询也是正确的,就可以直接存取对象了,说明自定义数据类型正确
Depart depart = new Depart(); depart.setName("技术前沿研究所"); userDao.insertDepart(depart); depart.setId("402881eb4371f277014371f358b70000"); Emp emp = new Emp(); emp.setName("silentwu"); emp.setAge(21); emp.setDepart(depart); empDao.insertEmp(emp); //保存emp //查询emp Emp emp= empDao.queryEmp("402881eb4372033c01437203744f0000"); System.out.println(emp);
测是结果正确。