文章目录
- 1、需求
- 2、案例效果
- 3、案例分析
- 4、自定义JDBC框架-代码实现
- 4.1、Configuration
- 4.2、注解
- 4.3、映射类:Mapper
- 4.4、SqlSession类
- 5、自定义JDBC框架的使用
- 5.1、数据表
- 5.2、创建实体类
- 5.3、UserMapper接口
- 5.4、配置文件:config.xml
- 5.5、测试类
- 5.5、测试类
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);
}
}
}