封装JDBC:实现简单ORM框架lfdb
一、简介
框架就是一组可重用的构件,LZ自己写的姑且就叫微型小框架:lfdb。LZ也对其他的ORM框架没有什么了解,现在只会一个Hibernate,还是勉强会,什么懒加载,什么二级缓存这些太高级了,平时也没用到,但是用了就要明白个所以然,自己揣摩着模仿写个小框架,但是没有研究过Hibernate是怎么写的,也不清楚系统的架构,凭借自己的感觉写的,很多地方理解上有错,很多代码写得也很垃圾,还没很多东西没有考虑到,比如当个表的映射关系,数据库外键的关联等等。希望各位大神给与一点点指点。
二、结构
1、Configuration:配置文件类,加载并解析配置文件,生成实例化的SessionFactory。
2、SessionFactory:接口,加载数据库驱动,生成Session放入SessionPool(池)中,提供Session。
>>具体实现:SessionFactoryImpl
3、Session:接口,提供事务管理,包含对象的增删改查,以及sql执行。
>>具体实现:SessionImpl
4、SQLBuilder:接口,创建增删改差的sql语句。可以针对不同的数据库设计不同的实现。
>>具体实现:Mysql SQLBuilder
三、使用
一个东西,要想明白他的原理,必须先要知道怎么使用:
- 创建Configuration对象:构造时加载配置文件。
- 使用Configuration对象创建一个SessionFactory对象:configuration.buildSessionFactory()。
- 获取Session。
- 使用session执行操作。
- 关闭session。
代码如下:
1 public static void main(String[] args) { 2 //生成配置对象 3 Configuration configuration=new Configuration("dbtest/test/config.xml"); 4 //生成Session工厂 5 SessionFactory sessionFactory=configuration.buildSessionFactory(); 6 //获取Session 7 Session session=sessionFactory.getSession(); 8 Student student=new Student(); 9 student.setSex("男"); 10 student.setSname("德玛西亚"); 11 student.setCollege("超神学院"); 12 student.setSno("1212121"); 13 //执行事务 14 session.add(student); 15 //关闭Session 16 session.colse(); 17 }
四、实现
1、 Configuration类实现
1 /** 2 * Configuration:参数配置类 3 * 4 * @author ZWQ 5 * @version 1.0 6 * <p> 7 * 通过该类使用配置文件建立SessionFactory。 8 * </p> 9 * **/ 10 public class Configuration { 11 //数据库驱动 12 private String driver = ""; 13 //连接url 14 private String url = ""; 15 //用户名 16 private String user = ""; 17 //密码 18 private String password = ""; 19 //Session池初始大小 20 private int initsize = 5; 21 //Session池最大大小 22 private int maxsize = 10; 23 24 /** 25 * 默认构造方法,使用项目根目录src下面的lfdb.config.xml文件 26 */ 27 public Configuration() { 28 initConfig("lfdb.config.xml"); 29 } 30 31 /** 32 * 带参构造方法,使用项目自定义的.xml文件 33 * <p> 34 * 根目录下使用为Configuration configuration=new Configuration("config.xml"); 35 * </p> 36 * <p> 37 * 具体包下面使用为Configuration configuration=new Configuration("demo/config.xml"); 38 * </p> 39 * 40 * @param configFile 41 * :String 需要使用的lfdb配置文件 42 */ 43 public Configuration(String configFile) { 44 initConfig(configFile); 45 } 46 47 private void initConfig(String configFile) { 48 try { 49 // 获取配置文件输入流 50 InputStream configInputStream = getClass().getClassLoader().getResourceAsStream(configFile); 51 if (configInputStream == null) { 52 System.out.println(">>>>>>>配置文件未找到"); 53 new FileNotFoundException(); 54 } 55 // XML文件解析 56 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 57 dbf.setIgnoringComments(true); 58 dbf.setIgnoringElementContentWhitespace(true); 59 DocumentBuilder db = dbf.newDocumentBuilder(); 60 System.out.println(">>>>>>>解析配置文件..."); 61 Document document = db.parse(configInputStream); 62 Element root = document.getDocumentElement(); 63 NodeList config = root.getChildNodes(); 64 // 读取配置参数内容 65 for (int i = 0; i < config.getLength(); i++) { 66 Node node = config.item(i); 67 String nodeName = node.getNodeName(); 68 if (nodeName.equalsIgnoreCase("driver")) { 69 driver = node.getFirstChild().getNodeValue().trim(); 70 } else if (nodeName.equalsIgnoreCase("url")) { 71 url = node.getFirstChild().getNodeValue().trim(); 72 } else if (nodeName.equalsIgnoreCase("user")) { 73 user = node.getFirstChild().getNodeValue().trim(); 74 } else if (nodeName.equalsIgnoreCase("password")) { 75 password = node.getFirstChild().getNodeValue().trim(); 76 } else if (nodeName.equalsIgnoreCase("initsize")) { 77 initsize = Integer.parseInt(node.getFirstChild().getNodeValue().trim()); 78 } else if (nodeName.equalsIgnoreCase("maxsize")) { 79 maxsize = Integer.parseInt(node.getFirstChild().getNodeValue().trim()); 80 } 81 } 82 System.out.println(">>>>>>>配置文件解析完成"); 83 } catch (Exception e) { 84 e.printStackTrace(); 85 new RuntimeException(); 86 } 87 } 88 89 /** 90 * 建立一个SessionFactory 91 * 92 * @return SessionFactory 返回一个SessionFactory的实例 93 * **/ 94 public SessionFactory buildSessionFactory() { 95 return new SessionFactoryImpl(this); 96 }
2、 SessionFactoryImpl类实现
1 public class SessionFactoryImpl implements SessionFactory { 2 // Session池 3 LinkedList<Session> sessionPool = new LinkedList<Session>(); 4 // 与Configuration中参数相对应 5 private String driver; 6 private String url; 7 private String user; 8 private String password; 9 private int initsize; 10 private int maxsize; 11 // 当前Session池最大大小 12 private int currentsize; 13 14 // 通过Configuration构造,并加载驱动,初始化Session池 15 public SessionFactoryImpl(Configuration configuration) { 16 driver = configuration.getDriver(); 17 url = configuration.getUrl(); 18 user = configuration.getUser(); 19 password = configuration.getPassword(); 20 initsize = configuration.getInitsize(); 21 maxsize = configuration.getMaxsize(); 22 loadDriver(); 23 for (int i = 0; i < initsize; i++) { 24 createSession(); 25 } 26 } 27 28 // 创建Session,放入Session池 29 private void createSession() { 30 if (currentsize < maxsize) { 31 try { 32 Connection connection = DriverManager.getConnection(url, user, password); 33 //当前只支持MySql语句的生成 34 SQLBuilder sqlBuilder = new MysqlSQLBuilder(); 35 sessionPool.addLast(new SessionImpl(connection, sqlBuilder, sessionPool)); 36 currentsize++; 37 } catch (Exception e) { 38 System.out.println(">>>>>>>创建Session出错"); 39 e.printStackTrace(); 40 } 41 42 } else { 43 System.out.println(">>>>>>>已超出Session配置最大容量"); 44 } 45 } 46 47 // 加载数据库驱动 48 private void loadDriver() { 49 System.out.println(">>>>>>>加载数据库驱动..."); 50 try { 51 // 加载数据库驱动. 52 Class.forName(driver); 53 System.out.println(">>>>>>>加载数据库驱动成功..."); 54 } catch (ClassNotFoundException e) { 55 System.out.println(">>>>>>>加载数据库驱动失败..."); 56 e.printStackTrace(); 57 new RuntimeException(); 58 } 59 } 60 61 @Override 62 public Session getSession() { 63 synchronized (sessionPool) { 64 if (this.sessionPool.size() > 0) { 65 return this.sessionPool.removeFirst(); 66 } else { 67 createSession(); 68 if (this.sessionPool.size() > 0) { 69 return this.sessionPool.removeFirst(); 70 } else { 71 System.out.println(">>>>>>>>已经没有session"); 72 return null; 73 } 74 } 75 } 76 } 77 78 @Override 79 public void closeSession(Session session) { 80 sessionPool.addLast(session); 81 session=null; 82 } 83 }
3、 SessionImpl类实现:部分代码
1 @Override 2 public void add(Object object) { 3 try { 4 String sql = sqlBuilder.createAddSQL(object); 5 Statement statement = connection.createStatement(); 6 statement.executeUpdate(sql); 7 } catch (Exception e) { 8 System.out.println(">>>>>>>>添加对象失败"); 9 e.printStackTrace(); 10 } 11 } 12 @Override 13 public <T> List<T> get(Class<T> clazz, String sql) { 14 List<T> result = null; 15 try { 16 Statement statement = connection.createStatement(); 17 ResultSet rs = statement.executeQuery(sql); 18 BasicRowProcessor basicRowProcessor = new BasicRowProcessor(); 19 result = basicRowProcessor.toBeanList(rs, clazz); 20 } catch (SQLException e) { 21 System.out.println(">>>>>>>获取结果出错"); 22 e.printStackTrace(); 23 } 24 return result; 25 }
4、 MysqlSQLBuilder类实现:部分代码
1 @Override 2 public String createAddSQL(Object object) { 3 StringBuilder sql = new StringBuilder(); 4 StringBuilder columns = new StringBuilder(); 5 StringBuilder values = new StringBuilder(); 6 sql.append("insert into "); 7 sql.append(object.getClass().getSimpleName()); 8 9 try { 10 Field[] fields = object.getClass().getDeclaredFields(); 11 boolean firststate = false; 12 for (Field field : fields) { 13 field.setAccessible(true); 14 if (field.getName().toString().toLowerCase().equals("id")) { 15 continue; 16 } 17 if (firststate) { 18 columns.append(","); 19 values.append(","); 20 21 } else { 22 firststate = true; 23 } 24 String column = field.getName(); 25 Object value = field.get(object); 26 columns.append(column); 27 if (field.getType() == String.class) { 28 values.append("‘"); 29 values.append(value); 30 values.append("‘"); 31 } else { 32 values.append(value); 33 } 34 } 35 sql.append("("); 36 sql.append(columns); 37 sql.append(") values("); 38 sql.append(values); 39 sql.append(")"); 40 } catch (IllegalArgumentException | IllegalAccessException e) { 41 System.out.println(">>>>>>>创建插入sql语句失败"); 42 e.printStackTrace(); 43 } 44 return sql.toString(); 45 }
五、总结
写一个框架是需要用心的事情,需要考虑到使用者的方便性,以及功能的完整性与健壮性。
这个只是一个半成品,很多地方还没有实现,bug也不少,性能就更加不要说了,写这个是为了学习,很多地方理解有误的,还望各路大神指出。
六、附件
下载地址:http://pan.baidu.com/s/1i35hd1j
文件说明:
1、lfdb源码.zip :lfdb的源代码
2、lfdb_1.2.jar :可以直接使用的jar包
3、lfdbdemo源码.zip :lfdb示例的源代码