package cn.itcast.config;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import javax.sql.DataSource;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
/*
配置类
1. 解析XML文件
2. 创建德鲁伊连接池
*/
public class Configuration {
/* 定义数据库连接对象相关属性 */
private String driver;//驱动 com.mysql.jdbc.Driver
private String url;//连接地址 jdbc:mysql://127.0.0.1:3306/heima02
private String username;//登录名 root
private String password;//密码 1234
/* Mapper接口的全名称 */
private String interName;//"cn.itcast.mapper.UserMapper"
/* 数据库连接池对象 */
private DataSource dataSource;//连接池初始化成功
/* 映射类对象 */
//当前类Configuration加载创建Mapper对象
//Mapper映射类,该类中具有成员变量: private String sql;//存储sql语句 private String resultType;//结果类型
//private String sql="select * from user"
//private String resultType="cn.itcast.pojo.User"
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 {
//创建属性集对象
Properties p = new Properties();
//向属性集对象p中添加数据:
p.setProperty("url",url);//url=jdbc:mysql://127.0.0.1:3306/heima02
p.setProperty("username",username);//username=root
p.setProperty("password",password);//password=1234
p.setProperty("driverClassName",driver);//driverClassName=com.mysql.jdbc.Driver
//获取数据库连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(p);
//将ds赋值给成员变量dataSource
this.dataSource = ds;//数据库连接池对象
}
//将核心配置文件中的数据封装到Configuration类属性中
private void loadConfigXml(Document doc) {
/*
//使用document对象调用方法获取property标签:
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.01:3306/itheima"/>
<property name="username" value="root"/>
<property name="password" value="itheima"/>
*/
List<Node> list = doc.selectNodes("//property");
//遍历List集合
for (Node node : list) {
//强制转换
//Node使用Element的父接口
Element e = (Element) node;// <property name="driver" value="com.mysql.jdbc.Driver"/>
//获取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":
//将从config.xml文件中获取的值com.mysql.jdbc.Driver赋值给成员变量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" cn.itcast.mapper.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;
}
}
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;
}
}
package cn.itcast.config类;
import cn.itcast.anno.ResultType;
import cn.itcast.anno.Select;
import cn.itcast.mapper.UserMapper;
import cn.itcast.pojo.User;
import javax.management.relation.Role;
import javax.sql.DataSource;
import java.lang.reflect.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("all")
public class SqlSession {
/**
* 动态代理:
* 生成接口UserMapper的代理对象,将代理对象返回给调用者
* UserMapper mapper = sqlSession.getMapper(UserMapper.class);
* 说明:
* 1.这里使用自定义泛型方法,调用传递是什么类型,那么泛型就是什么类型,那么这里返回的类型使用自定义泛型,所有返回的类型也是
* 传递的类型
*/
public <T> T getMapper(Class<T> clazz) {//Class<T> clazz = UserMapper.class
//1.使用Proxy类调用静态方法生成接口xxxMapper(例如UserMapper)的代理对象proxyMapper
//1.1类加载器:加载代理类和接口
ClassLoader loader = clazz.getClassLoader();
//1.2接口,这里代理的是接口
// Class<?>[] interfaces = {UserMapper.class}; 不推荐使用,因为书写固定了
Class<?>[] interfaces = {clazz};
//1.3调用处理程序即InvocationHandler的对象
InvocationHandler h = new InvocationHandler(){
/*
说明:
1)在测试类中使用返回的代理对象mapper调用UserMapper接口中的方法queryAllUser(),那么
就会执行当前接口InvocationHandler的invoke方法List<User> users = mapper.queryAllUser();
2)
Object proxy 代理对象 不用
Method method 接口增强的方法 public abstract List queryAllUser();
Object[] args:增强方法的实参
3)
测试类代码: List<User> users = mapper.queryAllUser();
要求在invoke方法体中使用jdbc技术查询数据表所有数据封装到List集合中,最后返回的是List集合
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.创建配置类对象,对象创建好之后Configuration类的成员变量都已经别赋值了
Configuration config = new Configuration();
//2.解析UserMapper接口中queryAllUser方法上的注解 @Select("select * from user") 和
// @ResultType("cn.itcast.pojo.User")
//2.1获取UserMapper接口中的方法 Class<T> clazz = UserMapper.class
Method[] methods = clazz.getMethods();
//2.2遍历数组取出每个方法
for (Method m : methods) {//m 就是接口UserMapper中的方法 public abstract List queryAllUser();
//2.3判断方法上是否有注解
boolean boo1 = m.isAnnotationPresent(Select.class);
boolean boo2 = m.isAnnotationPresent(ResultType.class);
if(boo1 && boo2){
//2.4说明获取的方法上有两个注解@Select @ResultType,开始解析注解
Select select = m.getAnnotation(Select.class);
ResultType resultType = m.getAnnotation(ResultType.class);
//2.5获取注解的属性值
String[] sqlArr = select.value();//{"select * from user"}
String resultTypeValue = resultType.value();//"cn.itcast.pojo.User"
//2.6获取映射类Mapper的对象
Mapper mapper = config.getMapper();
//2.7将解析出的注解数据存放到mapper对象中
mapper.setSql(sqlArr[0]);
mapper.setResultType(resultTypeValue);
}
}
//3.获取数据库连接池对象
DataSource ds = config.getDataSource();
//4.从数据库连接池中获取连接
Connection conn = ds.getConnection();
//5.获取发送给数据库的sql语句
Mapper mapper = config.getMapper();
String sql = mapper.getSql();//"select * from user"
//6.获取封装数据表数据的结果类型
String resultType = mapper.getResultType();//"cn.itcast.pojo.User"
//7.获取结果类型的Class对象 Class.forName("cn.itcast.pojo.User");
Class<?> resultTypeClazz = Class.forName(resultType);
//8.调用方法使用jdbc技术查询数据表数据封装到List集合中
List list = queryForListUsers(conn,sql,resultTypeClazz);
//9.将存储数据的list集合对象返回给调用者 List<User> users = mapper.queryAllUser();
return list;
}
/*
String sql = "select * from user"
Class<?> resultTypeClazz = User.class
以下方法功能:使用jdbc技术查询数据表数据封装到List集合中
*/
private List queryForListUsers(Connection conn, String sql, Class<?> resultTypeClazz) throws Exception {
//1.创建List集合对象
ArrayList list = new ArrayList();
//2.根据连接对象获取预编译对象
PreparedStatement pst = conn.prepareStatement(sql);
//3.执行sql
ResultSet rs = pst.executeQuery();
//4.处理结果集
while(rs.next()){
//5.创建实体类对象
//User user = new User();
Object obj = resultTypeClazz.newInstance();//调用的是实体类中无参构造方法
//6.获取rs结果集对象中的数据
//getObject(列名/别名/第几列的编号) 可以获取数据表的任意类型的数据varchar int double date
//getObject(列名/别名/第几列的编号) 参数现在不知道书写啥了,必须保证通用性,适合所有的数据表
/*
规定:
数据表的字段名和类型要和实体类的成员变量名以及类型一致(ORM映射关系)。
举例:
数据表字段名:id 实体类成员变量名 : id
数据表字段名:name 实体类成员变量名 : name
*/
//6.1获取实体类所有的成员变量
Field[] fields = resultTypeClazz.getDeclaredFields();
//6.2遍历数组取出每个成员变量所属类型是Field类型
for (Field f : fields) {//f就是实体类的成员变量id name passwd age gender adddate
//6.3获取每个成员变量名
String fieldName = f.getName();//fieldName表示是成员变量名:"id" "name" "passwd" "age" "gender" "adddate"
//6.4根据下述方法结合成员变量名获取数据表的字段值
Object tableValue = rs.getObject(fieldName);//1 itcast 123123....
//6.5暴力反射
f.setAccessible(true);
//6.6调用Field类中的set方法给成员变量赋值
f.set(obj,tableValue);
}
//7.将实体类对象放到list集合中
list.add(obj);
}
//返回集合
return list;
}
};
T proxyMapper = (T)Proxy.newProxyInstance(loader, interfaces, h);
//2.将生成的代理对象proxyMapper返回给当前方法getMapper的调用者即在测试类中:
//UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return proxyMapper;
}
}