1、需求
需求:自定义dao层jdbc框架
- 为了方便程序员操作数据库,让程序员更关注于sql代码层面和业务层面
2、案例效果
使用到的技术:
- 反射
- 注解
- 动态代理
- xml解析:xpath
3、案例分析
自定义jdbc框架开发步骤:
1、通过软配置方式,和数据库连接
解析xml配置文件,获得:driver、url、username、password
德鲁伊连接池
根据配置文件中的参数,创建连接池对象
2、创建@Select注解
解析@Select注解中value值:select查询语句
3、创建映射类Mapper
把从@Select注解中解析出来的select查询语句,赋值给Mapper中的sql成员变量
4、创建SqlSession类
提供getMapper()方法,用来获取代理对象
说明:程序员在获取到代理对象后,利用代理对象调用某个方法时,会被代理对象拦截处理
4、自定义JDBC框架-代码实现
4.1、Configuration
/*配置类 1. 解析XML文件 2. 创建德鲁伊连接池 */ public class Configuration { /* 定义数据库连接对象相关属性 */ private String driver;//驱动 private String url;//连接地址 private String username;//登录名 private String password;//密码 /* Mapper接口的全名称 */ private String interName; /* 数据库连接池对象 */ private DataSource dataSource; /* 映射类对象 */ private Mapper mapper = new Mapper(); //无参构造方法 public Configuration() { try { //解析"config.xml"文件 SAXReader reader = new SAXReader(); InputStream is = Configuration.class.getClassLoader().getResourceAsStream("config.xml"); Document doc = reader.read(is); //调用自定义方法: 将核心配置文件中的数据封装到Configuration类的属性中 loadConfigXml(doc); //调用自定义方法: 初始化数据库连接池对象 createDataSource(); } catch (Exception e) { e.printStackTrace(); } } //初始化数据库连接池对象 private void createDataSource() throws Exception { //创建c3p0核心类对象 ComboPooledDataSource cpds = new ComboPooledDataSource(); //使用对象调用方法将四大连接参数给数据库连接池 cpds.setDriverClass(driver); cpds.setJdbcUrl(url); cpds.setUser(username); cpds.setPassword(password); //将cpds赋值给成员变量ds this.dataSource = cpds;//数据库连接池对象 } //将核心配置文件中的数据封装到Configuration类属性中 private void loadConfigXml(Document doc) { /* //使用document对象调用方法获取property标签: <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/itheima"/> <property name="username" value="root"/> <property name="password" value="itheima"/> */ List<Node> list = doc.selectNodes("//property"); //遍历List集合 for (Node node : list) { //强制转换 Element e = (Element) node; //获取property标签的name属性值 String name = e.attributeValue("name");//双引号中的name是property标签的name属性 driver //获取property标签的value属性值 String value = e.attributeValue("value");//双引号中的value是property标签的value属性 com.mysql.jdbc.Driver //将value的值赋值给成员变量 switch (name) { case "driver": this.driver = value;//数据库驱动 break; case "url": this.url = value;//连接url break; case "username": this.username = value;//登录名 break; case "password": this.password = value;//密码 break; } } //<package name="xxx.xxx.UserMapper"></package> Node node = doc.selectSingleNode("//package"); Element e = (Element) node; //Mapper接口的全名称 this.interName = e.attributeValue("name");//"xxx.xxx.UserMapper" } 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; } public String getInterName() { return interName; } public void setInterName(String interName) { this.interName = interName; } public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public Mapper getMapper() { return mapper; } public void setMapper(Mapper mapper) { this.mapper = mapper; } }
4.2、注解
@Select
//查询时使用的注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Select { String[] value(); }
@ResultType
//查询结果的封装类型 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface ResultType { String value(); }
4.3、映射类:Mapper
package cn.itcast.config; public class Mapper { private String sql;//存储sql语句 private String resultType;//结果类型 public Mapper() { } public Mapper(String sql) { this.sql = sql; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } }
4.4、SqlSession类
@SuppressWarnings("all") public class SqlSession { /** * 动态代理 */ public <T> T getMapper(Class<T> clazz) { //类加载器: 负责加载代理类到内存中 ClassLoader classLoader = SqlSession.class.getClassLoader(); //父接口 Class[] interfaces = {clazz}; T mapperProxy = (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { //在调用方法时,代理对象执行invoke方法,返回List @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //创建核心配置类对象 Configuration config = new Configuration(); /******** 解析 @Select、@ResultType *******/ Class clazz = Class.forName(config.getInterName()); Method[] methods = clazz.getMethods(); //遍历数组 for (Method m : methods) { //判断是否有注解 boolean boo = m.isAnnotationPresent(Select.class); boolean boo2 = m.isAnnotationPresent(ResultType.class); if (boo && boo2) { //获取@Select注解对象 Select select = m.getAnnotation(Select.class); //获取属性值 String[] value = select.value();//{"select * from user"} String sql = value[0]; //给Mapper对象中的sql成员变量赋值 config.getMapper().setSql(sql); //获取@ResultType注解对象 ResultType resultType = m.getAnnotation(ResultType.class); String type = resultType.value();//获取属性值 config.getMapper().setResultType(type); } } /*******************************/ //获取映射对象 Mapper mapper = config.getMapper(); //利用映射对象,获取sql语句 String sql = mapper.getSql(); //利用映射对象,获取类型 String resultType = mapper.getResultType(); Class cl = Class.forName(resultType); //获取数据库连接池对象 DataSource ds = config.getDataSource(); //利用连接池对象,获取Connection对象 Connection conn = ds.getConnection(); //调用自定义方法: 执行sql查询语句 List list = queryForList(conn, sql, cl); return list; } }); //代理对象返回给getMapper的调用者 UserMapper mapper = sqlSession.getMapper(UserMapper.class);//mapperProxy return mapperProxy; } public List queryForList(Connection conn, String sql, Class clazz) throws SQLException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { List userList = new ArrayList(); //通过连接对象得到预编译的语句对象 PreparedStatement ps = conn.prepareStatement(sql); //执行SQL语句,得到结果集 ResultSet rs = ps.executeQuery(); while (rs.next()) { //获取构造方法对象,并实例化 Object user = clazz.getConstructor().newInstance(); //获取所有的成员变量(包含私有成员变量) Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { //field可以是:id name passwd age gender adddate //得到成员变量的名字 String name = field.getName(); //暴力破解: 取消权限检查 field.setAccessible(true); //rs.getObject(name) 表示根据数据表的列名取出数据表中的列值 因为User类中的成员变量名必须和数据表列名一致 //例如: name 的值是birthday 那么这里 rs.getObject(name)---》rs.getObject("age")获取数据表的年龄20 Object table_value = rs.getObject(name); //void set(Object obj, Object value)给成员变量赋值,参数1:对象名 参数2:要赋的值 field.set(user, table_value); } //user对象添加到集合中 userList.add(user); } //释放资源 rs.close(); ps.close(); conn.close(); //返回集合 return userList; } }
5、自定义JDBC框架的使用
5.1、数据表
CREATE TABLE user ( id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, name varchar(30) DEFAULT NULL, passwd varchar(20) DEFAULT NULL, age int(3) DEFAULT NULL, gender varchar(2) DEFAULT NULL, adddate date DEFAULT NULL ); # 测试数据 INSERT INTO user VALUES (null, 'itcast', '123123', '10', '男', '2020-12-11');
5.2、创建实体类
public class User { private int id; private String name; private String passwd; private int age; private String gender; private Date adddate; public User() { } public User(int id, String name, String passwd, int age, String gender, Date adddate) { this.id = id; this.name = name; this.passwd = passwd; this.age = age; this.gender = gender; this.adddate = adddate; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPasswd() { return passwd; } public void setPasswd(String passwd) { this.passwd = passwd; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public Date getAdddate() { return adddate; } public void setAdddate(Date adddate) { this.adddate = adddate; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", passwd='" + passwd + '\'' + ", age=" + age + ", gender='" + gender + '\'' + ", adddate=" + adddate + '}'; } }
5.3、UserMapper接口
public interface UserMapper { //查询所有用户 @Select("select * from user") @ResultType("com.itcast.pojo.User") public abstract List queryAllUser(); }
5.4、配置文件:config.xml
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <!--数据源--> <dataSource> <!--驱动--> <property name="driver" value="com.mysql.jdbc.Driver"/> <!--地址--> <property name="url" value="jdbc:mysql://127.0.0.1:3306/itheima"/> <!--用户名--> <property name="username" value="root"/> <!--密码--> <property name="password" value="itheima"/> </dataSource> <!--加载映射接口--> <mappers> <package name="com.itcast.mapper.UserMapper"></package> </mappers> </configuration>
5.5、测试类
public class Test1 { @Test public void testSelect() { //实例化 SqlSession sqlSession = new SqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.queryAllUser(); for (User u : users) { System.out.println(u); } } }
assword" value=“itheima”/>
<!--加载映射接口--> <mappers> <package name="com.itcast.mapper.UserMapper"></package> </mappers>
5.5、测试类
public class Test1 { @Test public void testSelect() { //实例化 SqlSession sqlSession = new SqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.queryAllUser(); for (User u : users) { System.out.println(u); } } }