/*** 用于封装执行的SQL语句和结果类型的全限定类名*/public class Mapper {private String queryString;//SQLprivate String resultType;//全限定实体类名称public String getQueryString() {return queryString;}public void setQueryString(String queryString) {this.queryString = queryString;}public String getResultType() {return resultType;}public void setResultType(String resultType) {this.resultType = resultType;}}
配置类
/*** mybatis的配置类*/public class Configuration {private String driver;private String url;private String username;private String password;private Map<String,Mapper> mappers = new HashMap<String,Mapper>();public Map<String, Mapper> getMappers() {return mappers;}public void setMappers(Map<String, Mapper> mappers) {System.out.println(mappers);this.mappers.putAll(mappers);// 追加}public String getDriver() {return driver;}public void setDriver(String driver) {this.driver = driver;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}
注解
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface Select {String value();}
数据库工具类
/*** 用于创建数据源的工具类*/public class DataSourceUtil {/*** 用于获取一个连接* @param cfg* @return*/public static Connection getConnection(Configuration cfg){try {Class.forName(cfg.getDriver());return DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());}catch(Exception e){throw new RuntimeException(e);}}}
负责执行SQL语句,并且封装结果集
/*** 负责执行SQL语句,并且封装结果集*/public class Executor {public <E> List<E> selectList(Mapper mapper, Connection conn) {PreparedStatement pstm = null;ResultSet rs = null;try {//1.取出mapper中的数据String queryString = mapper.getQueryString();//select * from userString resultType = mapper.getResultType();//com.itheima.domain.UserClass domainClass = Class.forName(resultType);//2.获取PreparedStatement对象pstm = conn.prepareStatement(queryString);//3.执行SQL语句,获取结果集rs = pstm.executeQuery();//4.封装结果集List<E> list = new ArrayList<E>();//定义返回值while(rs.next()) {//实例化要封装的实体类对象E obj = (E)domainClass.newInstance();//取出结果集的元信息:ResultSetMetaDataResultSetMetaData rsmd = rs.getMetaData();//取出总列数int columnCount = rsmd.getColumnCount();//遍历总列数for (int i = 1; i <= columnCount; i++) {//获取每列的名称,列名的序号是从1开始的String columnName = rsmd.getColumnName(i);//根据得到列名,获取每列的值Object columnValue = rs.getObject(columnName);//给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装)PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种//获取它的写入方法Method writeMethod = pd.getWriteMethod();//把获取的列的值,给对象赋值System.out.println(obj);System.out.println(columnValue);writeMethod.invoke(obj,columnValue);}//把赋好值的对象加入到集合中list.add(obj);}return list;} catch (Exception e) {e.printStackTrace();throw new RuntimeException(e);} finally {release(pstm,rs);}}private void release(PreparedStatement pstm,ResultSet rs){if(rs != null){try {rs.close();}catch(Exception e){e.printStackTrace();}}if(pstm != null){try {pstm.close();}catch(Exception e){e.printStackTrace();}}}}
xml解析配置文件
/*** 用于解析配置文件*/public class XMLConfigBuilder {/*** 解析主配置文件,把里面的内容填充到DefaultSqlSession所需要的地方* 使用的技术:* dom4j+xpath*/public static Configuration loadConfiguration(InputStream config){try{//定义封装连接信息的配置对象(mybatis的配置对象)Configuration cfg = new Configuration();//1.获取SAXReader对象SAXReader reader = new SAXReader();//2.根据字节输入流获取Document对象Document document = reader.read(config);//3.获取根节点Element root = document.getRootElement();//4.使用xpath中选择指定节点的方式,获取所有property节点List<Element> propertyElements = root.selectNodes("//property");//5.遍历节点for(Element propertyElement : propertyElements){//判断节点是连接数据库的哪部分信息//取出name属性的值String name = propertyElement.attributeValue("name");if("driver".equals(name)){//表示驱动//获取property标签value属性的值String driver = propertyElement.attributeValue("value");cfg.setDriver(driver);}if("url".equals(name)){//表示连接字符串//获取property标签value属性的值String url = propertyElement.attributeValue("value");cfg.setUrl(url);}if("username".equals(name)){//表示用户名//获取property标签value属性的值String username = propertyElement.attributeValue("value");cfg.setUsername(username);}if("password".equals(name)){//表示密码//获取property标签value属性的值String password = propertyElement.attributeValue("value");cfg.setPassword(password);}}//取出mappers中的所有mapper标签,判断他们使用了resource还是class属性List<Element> mapperElements = root.selectNodes("//mappers/mapper");//遍历集合for(Element mapperElement : mapperElements){//判断mapperElement使用的是哪个属性Attribute attribute = mapperElement.attribute("resource");if(attribute != null){System.out.println("使用的是XML");//表示有resource属性,用的是XML//取出属性的值String mapperPath = attribute.getValue();//获取属性的值"com/itheima/dao/IUserDao.xml"//把映射配置文件的内容获取出来,封装成一个mapMap<String,Mapper> mappers = loadMapperConfiguration(mapperPath);//给configuration中的mappers赋值cfg.setMappers(mappers);}else{System.out.println("使用的是注解");//表示没有resource属性,用的是注解//获取class属性的值String daoClassPath = mapperElement.attributeValue("class");//根据daoClassPath获取封装的必要信息Map<String,Mapper> mappers = loadMapperAnnotation(daoClassPath);//给configuration中的mappers赋值cfg.setMappers(mappers);}}//返回Configurationreturn cfg;}catch(Exception e){throw new RuntimeException(e);}finally{try {config.close();}catch(Exception e){e.printStackTrace();}}}/*** 根据传入的参数,解析XML,并且封装到Map中* @param mapperPath 映射配置文件的位置* @return map中包含了获取的唯一标识(key是由dao的全限定类名和方法名组成)* 以及执行所需的必要信息(value是一个Mapper对象,里面存放的是执行的SQL语句和要封装的实体类全限定类名)*/private static Map<String,Mapper> loadMapperConfiguration(String mapperPath)throws IOException {InputStream in = null;try{//定义返回值对象Map<String,Mapper> mappers = new HashMap<String,Mapper>();//1.根据路径获取字节输入流in = Resources.getResourceAsStream(mapperPath);//2.根据字节输入流获取Document对象SAXReader reader = new SAXReader();Document document = reader.read(in);//3.获取根节点Element root = document.getRootElement();//4.获取根节点的namespace属性取值String namespace = root.attributeValue("namespace");//是组成map中key的部分//5.获取所有的select节点List<Element> selectElements = root.selectNodes("//select");//6.遍历select节点集合for(Element selectElement : selectElements){//取出id属性的值 组成map中key的部分String id = selectElement.attributeValue("id");//取出resultType属性的值 组成map中value的部分String resultType = selectElement.attributeValue("resultType");//取出文本内容 组成map中value的部分String queryString = selectElement.getText();//创建KeyString key = namespace+"."+id;//创建ValueMapper mapper = new Mapper();mapper.setQueryString(queryString);mapper.setResultType(resultType);//把key和value存入mappers中mappers.put(key,mapper);}return mappers;}catch(Exception e){throw new RuntimeException(e);}finally{in.close();}}/*** 根据传入的参数,得到dao中所有被select注解标注的方法。* 根据方法名称和类名,以及方法上注解value属性的值,组成Mapper的必要信息* @param daoClassPath* @return*/private static Map<String,Mapper> loadMapperAnnotation(String daoClassPath)throws Exception{//定义返回值对象Map<String,Mapper> mappers = new HashMap<String, Mapper>();//1.得到dao接口的字节码对象Class daoClass = Class.forName(daoClassPath);//2.得到dao接口中的方法数组Method[] methods = daoClass.getMethods();//3.遍历Method数组for(Method method : methods){//取出每一个方法,判断是否有select注解boolean isAnnotated = method.isAnnotationPresent(Select.class);if(isAnnotated){//创建Mapper对象Mapper mapper = new Mapper();//取出注解的value属性值Select selectAnno = method.getAnnotation(Select.class);String queryString = selectAnno.value();mapper.setQueryString(queryString);//获取当前方法的返回值,还要求必须带有泛型信息Type type = method.getGenericReturnType();//List<User>//判断type是不是参数化的类型if(type instanceof ParameterizedType){//强转ParameterizedType ptype = (ParameterizedType)type;//得到参数化类型中的实际类型参数Type[] types = ptype.getActualTypeArguments();//取出第一个Class domainClass = (Class)types[0];//获取domainClass的类名String resultType = domainClass.getName();//给Mapper赋值mapper.setResultType(resultType);}//组装key的信息//获取方法的名称String methodName = method.getName();String className = method.getDeclaringClass().getName();String key = className+"."+methodName;//给map赋值mappers.put(key,mapper);}}return mappers;}}
