1. /**
    2. * 用于封装执行的SQL语句和结果类型的全限定类名
    3. */
    4. public class Mapper {
    5. private String queryString;//SQL
    6. private String resultType;//全限定实体类名称
    7. public String getQueryString() {
    8. return queryString;
    9. }
    10. public void setQueryString(String queryString) {
    11. this.queryString = queryString;
    12. }
    13. public String getResultType() {
    14. return resultType;
    15. }
    16. public void setResultType(String resultType) {
    17. this.resultType = resultType;
    18. }
    19. }

    配置类

    1. /**
    2. * mybatis的配置类
    3. */
    4. public class Configuration {
    5. private String driver;
    6. private String url;
    7. private String username;
    8. private String password;
    9. private Map<String,Mapper> mappers = new HashMap<String,Mapper>();
    10. public Map<String, Mapper> getMappers() {
    11. return mappers;
    12. }
    13. public void setMappers(Map<String, Mapper> mappers) {
    14. System.out.println(mappers);
    15. this.mappers.putAll(mappers);// 追加
    16. }
    17. public String getDriver() {
    18. return driver;
    19. }
    20. public void setDriver(String driver) {
    21. this.driver = driver;
    22. }
    23. public String getUrl() {
    24. return url;
    25. }
    26. public void setUrl(String url) {
    27. this.url = url;
    28. }
    29. public String getUsername() {
    30. return username;
    31. }
    32. public void setUsername(String username) {
    33. this.username = username;
    34. }
    35. public String getPassword() {
    36. return password;
    37. }
    38. public void setPassword(String password) {
    39. this.password = password;
    40. }
    41. }

    注解

    1. import java.lang.annotation.ElementType;
    2. import java.lang.annotation.Retention;
    3. import java.lang.annotation.RetentionPolicy;
    4. import java.lang.annotation.Target;
    5. @Retention(RetentionPolicy.RUNTIME)
    6. @Target(ElementType.METHOD)
    7. public @interface Select {
    8. String value();
    9. }

    数据库工具类

    1. /**
    2. * 用于创建数据源的工具类
    3. */
    4. public class DataSourceUtil {
    5. /**
    6. * 用于获取一个连接
    7. * @param cfg
    8. * @return
    9. */
    10. public static Connection getConnection(Configuration cfg){
    11. try {
    12. Class.forName(cfg.getDriver());
    13. return DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());
    14. }catch(Exception e){
    15. throw new RuntimeException(e);
    16. }
    17. }
    18. }

    负责执行SQL语句,并且封装结果集

    1. /**
    2. * 负责执行SQL语句,并且封装结果集
    3. */
    4. public class Executor {
    5. public <E> List<E> selectList(Mapper mapper, Connection conn) {
    6. PreparedStatement pstm = null;
    7. ResultSet rs = null;
    8. try {
    9. //1.取出mapper中的数据
    10. String queryString = mapper.getQueryString();//select * from user
    11. String resultType = mapper.getResultType();//com.itheima.domain.User
    12. Class domainClass = Class.forName(resultType);
    13. //2.获取PreparedStatement对象
    14. pstm = conn.prepareStatement(queryString);
    15. //3.执行SQL语句,获取结果集
    16. rs = pstm.executeQuery();
    17. //4.封装结果集
    18. List<E> list = new ArrayList<E>();//定义返回值
    19. while(rs.next()) {
    20. //实例化要封装的实体类对象
    21. E obj = (E)domainClass.newInstance();
    22. //取出结果集的元信息:ResultSetMetaData
    23. ResultSetMetaData rsmd = rs.getMetaData();
    24. //取出总列数
    25. int columnCount = rsmd.getColumnCount();
    26. //遍历总列数
    27. for (int i = 1; i <= columnCount; i++) {
    28. //获取每列的名称,列名的序号是从1开始的
    29. String columnName = rsmd.getColumnName(i);
    30. //根据得到列名,获取每列的值
    31. Object columnValue = rs.getObject(columnName);
    32. //给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装)
    33. PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种
    34. //获取它的写入方法
    35. Method writeMethod = pd.getWriteMethod();
    36. //把获取的列的值,给对象赋值
    37. System.out.println(obj);
    38. System.out.println(columnValue);
    39. writeMethod.invoke(obj,columnValue);
    40. }
    41. //把赋好值的对象加入到集合中
    42. list.add(obj);
    43. }
    44. return list;
    45. } catch (Exception e) {
    46. e.printStackTrace();
    47. throw new RuntimeException(e);
    48. } finally {
    49. release(pstm,rs);
    50. }
    51. }
    52. private void release(PreparedStatement pstm,ResultSet rs){
    53. if(rs != null){
    54. try {
    55. rs.close();
    56. }catch(Exception e){
    57. e.printStackTrace();
    58. }
    59. }
    60. if(pstm != null){
    61. try {
    62. pstm.close();
    63. }catch(Exception e){
    64. e.printStackTrace();
    65. }
    66. }
    67. }
    68. }

    xml解析配置文件

    1. /**
    2. * 用于解析配置文件
    3. */
    4. public class XMLConfigBuilder {
    5. /**
    6. * 解析主配置文件,把里面的内容填充到DefaultSqlSession所需要的地方
    7. * 使用的技术:
    8. * dom4j+xpath
    9. */
    10. public static Configuration loadConfiguration(InputStream config){
    11. try{
    12. //定义封装连接信息的配置对象(mybatis的配置对象)
    13. Configuration cfg = new Configuration();
    14. //1.获取SAXReader对象
    15. SAXReader reader = new SAXReader();
    16. //2.根据字节输入流获取Document对象
    17. Document document = reader.read(config);
    18. //3.获取根节点
    19. Element root = document.getRootElement();
    20. //4.使用xpath中选择指定节点的方式,获取所有property节点
    21. List<Element> propertyElements = root.selectNodes("//property");
    22. //5.遍历节点
    23. for(Element propertyElement : propertyElements){
    24. //判断节点是连接数据库的哪部分信息
    25. //取出name属性的值
    26. String name = propertyElement.attributeValue("name");
    27. if("driver".equals(name)){
    28. //表示驱动
    29. //获取property标签value属性的值
    30. String driver = propertyElement.attributeValue("value");
    31. cfg.setDriver(driver);
    32. }
    33. if("url".equals(name)){
    34. //表示连接字符串
    35. //获取property标签value属性的值
    36. String url = propertyElement.attributeValue("value");
    37. cfg.setUrl(url);
    38. }
    39. if("username".equals(name)){
    40. //表示用户名
    41. //获取property标签value属性的值
    42. String username = propertyElement.attributeValue("value");
    43. cfg.setUsername(username);
    44. }
    45. if("password".equals(name)){
    46. //表示密码
    47. //获取property标签value属性的值
    48. String password = propertyElement.attributeValue("value");
    49. cfg.setPassword(password);
    50. }
    51. }
    52. //取出mappers中的所有mapper标签,判断他们使用了resource还是class属性
    53. List<Element> mapperElements = root.selectNodes("//mappers/mapper");
    54. //遍历集合
    55. for(Element mapperElement : mapperElements){
    56. //判断mapperElement使用的是哪个属性
    57. Attribute attribute = mapperElement.attribute("resource");
    58. if(attribute != null){
    59. System.out.println("使用的是XML");
    60. //表示有resource属性,用的是XML
    61. //取出属性的值
    62. String mapperPath = attribute.getValue();//获取属性的值"com/itheima/dao/IUserDao.xml"
    63. //把映射配置文件的内容获取出来,封装成一个map
    64. Map<String,Mapper> mappers = loadMapperConfiguration(mapperPath);
    65. //给configuration中的mappers赋值
    66. cfg.setMappers(mappers);
    67. }else{
    68. System.out.println("使用的是注解");
    69. //表示没有resource属性,用的是注解
    70. //获取class属性的值
    71. String daoClassPath = mapperElement.attributeValue("class");
    72. //根据daoClassPath获取封装的必要信息
    73. Map<String,Mapper> mappers = loadMapperAnnotation(daoClassPath);
    74. //给configuration中的mappers赋值
    75. cfg.setMappers(mappers);
    76. }
    77. }
    78. //返回Configuration
    79. return cfg;
    80. }catch(Exception e){
    81. throw new RuntimeException(e);
    82. }finally{
    83. try {
    84. config.close();
    85. }catch(Exception e){
    86. e.printStackTrace();
    87. }
    88. }
    89. }
    90. /**
    91. * 根据传入的参数,解析XML,并且封装到Map中
    92. * @param mapperPath 映射配置文件的位置
    93. * @return map中包含了获取的唯一标识(key是由dao的全限定类名和方法名组成)
    94. * 以及执行所需的必要信息(value是一个Mapper对象,里面存放的是执行的SQL语句和要封装的实体类全限定类名)
    95. */
    96. private static Map<String,Mapper> loadMapperConfiguration(String mapperPath)throws IOException {
    97. InputStream in = null;
    98. try{
    99. //定义返回值对象
    100. Map<String,Mapper> mappers = new HashMap<String,Mapper>();
    101. //1.根据路径获取字节输入流
    102. in = Resources.getResourceAsStream(mapperPath);
    103. //2.根据字节输入流获取Document对象
    104. SAXReader reader = new SAXReader();
    105. Document document = reader.read(in);
    106. //3.获取根节点
    107. Element root = document.getRootElement();
    108. //4.获取根节点的namespace属性取值
    109. String namespace = root.attributeValue("namespace");//是组成map中key的部分
    110. //5.获取所有的select节点
    111. List<Element> selectElements = root.selectNodes("//select");
    112. //6.遍历select节点集合
    113. for(Element selectElement : selectElements){
    114. //取出id属性的值 组成map中key的部分
    115. String id = selectElement.attributeValue("id");
    116. //取出resultType属性的值 组成map中value的部分
    117. String resultType = selectElement.attributeValue("resultType");
    118. //取出文本内容 组成map中value的部分
    119. String queryString = selectElement.getText();
    120. //创建Key
    121. String key = namespace+"."+id;
    122. //创建Value
    123. Mapper mapper = new Mapper();
    124. mapper.setQueryString(queryString);
    125. mapper.setResultType(resultType);
    126. //把key和value存入mappers中
    127. mappers.put(key,mapper);
    128. }
    129. return mappers;
    130. }catch(Exception e){
    131. throw new RuntimeException(e);
    132. }finally{
    133. in.close();
    134. }
    135. }
    136. /**
    137. * 根据传入的参数,得到dao中所有被select注解标注的方法。
    138. * 根据方法名称和类名,以及方法上注解value属性的值,组成Mapper的必要信息
    139. * @param daoClassPath
    140. * @return
    141. */
    142. private static Map<String,Mapper> loadMapperAnnotation(String daoClassPath)throws Exception{
    143. //定义返回值对象
    144. Map<String,Mapper> mappers = new HashMap<String, Mapper>();
    145. //1.得到dao接口的字节码对象
    146. Class daoClass = Class.forName(daoClassPath);
    147. //2.得到dao接口中的方法数组
    148. Method[] methods = daoClass.getMethods();
    149. //3.遍历Method数组
    150. for(Method method : methods){
    151. //取出每一个方法,判断是否有select注解
    152. boolean isAnnotated = method.isAnnotationPresent(Select.class);
    153. if(isAnnotated){
    154. //创建Mapper对象
    155. Mapper mapper = new Mapper();
    156. //取出注解的value属性值
    157. Select selectAnno = method.getAnnotation(Select.class);
    158. String queryString = selectAnno.value();
    159. mapper.setQueryString(queryString);
    160. //获取当前方法的返回值,还要求必须带有泛型信息
    161. Type type = method.getGenericReturnType();//List<User>
    162. //判断type是不是参数化的类型
    163. if(type instanceof ParameterizedType){
    164. //强转
    165. ParameterizedType ptype = (ParameterizedType)type;
    166. //得到参数化类型中的实际类型参数
    167. Type[] types = ptype.getActualTypeArguments();
    168. //取出第一个
    169. Class domainClass = (Class)types[0];
    170. //获取domainClass的类名
    171. String resultType = domainClass.getName();
    172. //给Mapper赋值
    173. mapper.setResultType(resultType);
    174. }
    175. //组装key的信息
    176. //获取方法的名称
    177. String methodName = method.getName();
    178. String className = method.getDeclaringClass().getName();
    179. String key = className+"."+methodName;
    180. //给map赋值
    181. mappers.put(key,mapper);
    182. }
    183. }
    184. return mappers;
    185. }
    186. }