JDBC实现数据库连接

  1. Connection connection = null;
  2. PreparedStatement preparedStatement = null;
  3. ResultSet resultSet = null;
  4. try {
  5. // 1.获取mysql的驱动
  6. Class.forName("com.mysql.jdbc.Driver");
  7. // 2.获取数据库连接
  8. connection = DriverManager.getConnection("jdbc:mysql://localhost:3307/layui? characterEncoding=utf-8","root","root");
  9. String sql = "select * from student where id = ?";
  10. // 3.获取预处理来执行sql语句
  11. preparedStatement = connection.prepareStatement(sql);
  12. // 添加参数
  13. preparedStatement.setString(1,"3");
  14. // 4.执行sql返回结果
  15. resultSet = preparedStatement.executeQuery();
  16. int id = 0;
  17. String name = "";
  18. while (resultSet.next()) {
  19. id = resultSet.getInt("id");
  20. name = resultSet.getString("name");
  21. }
  22. System.out.println("id=" + id + ",name=" + name);
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. } finally {
  26. if (connection != null) {
  27. try {
  28. connection.close();
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. if (preparedStatement != null) {
  34. try {
  35. preparedStatement.close();
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. if (resultSet != null) {
  41. try {
  42. resultSet.close();
  43. } catch (Exception e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. }

JDBC的弊端

  1. 数据库的配置信息存在硬编码,如果更换数据库改动量过大。
    • 解决:配置文件,把信息写在配置文件中
  2. 频繁创建释放数据库连接,耗费资源
    • 解决:使用数据库连接池 c3p0 druid…
  3. sql语句,设置参数,获取结果集存在硬编码,sql变动需要改变java代码,造成代码不易维护
    • 解决:将sql语句抽取到xml配置文件中
    • 经常改变的信息跟不常改变的信息建议放在不同配置文件中
  4. 手动封装返回的结果集,如果返回值的属性过多,会非常繁琐

    • 解决:反射,内省等底层技术,自动将表与实体进行属性和字段的自动映射

      自定义持久层框架

      设计思路

  5. 使用端(项目):引入自定义持久层框架的jar包

    • 提供两部分的配置信息:数据库配置、sql配置
    • sql配置信息包括:sql语句、参数类型、返回值类型
    • 使用配置文件存放这两部分信息:
      • sqlMapConfig.xml:数据库配置信息,存储mapper.xml的全路径(框架不用加载两次路径)
      • mapper.xml:sql配置信息
  6. 自定义持久层框架本身(工程):本身是对jdbc代码的封装

    • 1)加载配置文件,根据配置文件的路径,加载配置文件成字节输入流,存储在内存中
      • 创建Resource类 方法:InputStream getResourceAsStream(String path)
    • 2)创建两个javaBean(容器对象):存放的就是对配置文件解析出来的内容
      • Configuration(核心配置类):存放sqlMapper.xml解析出来的内容
      • MappedStatement(映射配置类):存放mapper.xml解析出来的内容
    • 3)解析配置文件:dom4j
      • 创建类:SqlSessionFactoryBuilder 方法:build(InputStream in)
        • 第一:使用dom4j解析配置文件,解析的内容封装到容器对象中(第二步的两个javaBean)
        • 第二:创建SqlSessionFactory对象:生产sqlSession:会话对象(工厂模式)
    • 4)创建SqlSessionFactory接口以及实现类DefaultSqlSessionFactory
      • 方法:openSession():生成sqlSession
    • 5)创建Sqlsession接口以及实现类DefaultSession
      • 定义对数据库的crud操作:selectList()、selectOne()、update()、delete()
    • 6)创建Executor接口以及实现类SimpleExecutor实现类
      • query(Configuration,MappedStatement,Object…params):执行的就是jdbc代码

        问题分析

  7. Dao层使用自定义持久层框架,存在代码重复,整个操作的过程模板重复(加载配置文件,创建sqlSessionFactory,生成sqlSession)

  8. statementId存在硬编码问题
  9. 解决思路:
    1. 使用代理模式生成Dao层接口的代理实现类
    2. 代码 ```java // 此处userDao是proxy代理对象,代理对象调用接口中任意方法,都会执行invoke IUserDao userDao = sqlSession.getMapper(IUserDao.class); User all = userDao.findByCondition(user);

public T getMapper(Class<?> mapperClass) { // 使用jdk动态代理为Dao接口生成代理对象,并返回 Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() { // proxy:当前代理对象的引用 method:当前被调用方法的引用 args:传递的参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws SQLException, IntrospectionException, NoSuchFieldException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException { // 底层还是执行jdbc代码 String methodName = method.getName(); String className = method.getDeclaringClass().getName(); // 根据规则,namespace=类的全限定类名 id=方法名,所以statementId=className.methodName String statementId = className + “.” + methodName; // 获取被调用方法的返回值类型 Type returnType = method.getGenericReturnType(); // 判断是否进行了泛型类型参数化(返回类型是否存在泛型) if (returnType instanceof ParameterizedType) { List objects = selectList(statementId, args); return objects; } return selectOne(statementId,args); } }); return (T) proxyInstance; } ```