第一节 反射应用:完善DBUtil,提取select()
1.1 认识ResultSetMetaData
利用ResultSet的getMetaData的方法可以获得ResultSetMeta对象,而ResultSetMetaData存储了 ResultSet的MetaData。
所谓的MetaData在英文中的解释为“Data about Data”,直译成中文则为“有关数据的数据”或者“描述数据的数据”,一般翻译为“元数据”,实际上就是描述及解释含义的数据。
以Result的MetaData为例,ResultSet是以表格的形式存在,所以MetaData就包括了数据的字段名称、类型以及数目等表格所必须具备的信息。就是其结构信息(就是desc tablename所显示的内容)。
【示例1】认识ResultSetMetaData
public classTestResultSetMetaData {
public static voidmain(String[] args)throwsException {
//0.将相应数据库的jar包放入项目
Connection conn =null;
Statement stmt =null;
ResultSet rs =null;
intn =0;
//1.加载驱动(MySQL)
String driver ="com.mysql.jdbc.Driver";
String url ="jdbc:mysql://127.0.0.1:3306/stumgr";
String user ="root";
String password ="root";
Class.forName(driver);
//2.建立(和数据库)连接
conn = DriverManager.getConnection(url, user, password);
//3.创建一个SQL命令发送器
stmt = conn.createStatement();
//4.使用SQL命令发送器来发送SQL命令(子弹)并得到结果
//String sql = "select * from emp";
String sql ="select empno,ename,hiredate,sal from emp";
rs = stmt.executeQuery(sql);
//5.得到结果集的结构
ResultSetMetaData rsmd = rs.getMetaData();
System.out.println(rsmd.getColumnCount());
for(inti =0; i < rsmd.getColumnCount(); i++) {
System.out.println(rsmd.getColumnName(i +1) +"\t"
+ rsmd.getColumnTypeName(i +1) +"\t"
+ rsmd.getColumnClassName(i +1));
}
//6.关闭各种数据库资源
}
}
1.2 提取DBUtil的select()
【示例2】提取DBUtil类的查询方法
public abstract classDBUtil {
public static List executeQuery(String sql, Object params[], String className) {
Connection conn =null;
PreparedStatement pstmt =null;
ResultSet rs =null;
List list =newArrayList();
try{
//2.获取连接
conn = DBUtil.getConnection();
//3.创建Statement
pstmt = conn.prepareStatement(sql);
//4.使用Statement发送SQL命令并得到结果
for(inti =0; i < params.length; i++) {
pstmt.setObject(i +1, params[i]);
}
rs = pstmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
//5.处理结果(封装到List中)
while(rs.next()) {
//1.使用反射创建一个对象
//Employee Product Order News
Class clazz = Class.forName(className);
Tentity = (T) clazz.newInstance();
//2.取出当前行的某列并存入对象(使用反射调用方法)
for(inti =0; i < rsmd.getColumnCount(); i++) {
//1.获取结果集当前列的名称
String columnName = rsmd.getColumnName(i +1).toLowerCase(); // empno,mgr,sal
//2.根据当前列的名称或当前列的值
Object value = rs.getObject(columnName);// 7839 manager 5000
//3.通过反射调用方法entity.setEmpNo(value);
String methodName ="set"+ columnName.substring(0,1).toUpperCase()
+ columnName.substring(1);
Class paramType = Class.forName(rsmd.getColumnClassName(i+1));;
Method method = clazz.getMethod(methodName,paramType );
method.invoke(entity, value);
}
//3.将对象加入到集合中
list.add(entity);
}
}catch(Exception e) {
e.printStackTrace();
}finally{
//6.关闭资源
DBUtil.closeAll(rs, pstmt, conn);
}
//返回数据
returnlist;
}
public static List executeQuery(String sql,
Object params[], Class clazz) {
}
}
提取了两个重载的方法,区别在于第三个参数,可以传入类的完整的路径字符串,也可以直接传入类的Class信息。
1.3功能2:简化DAO的select方法
【示例3】简化后的DAO层查询代码
public classEmployeeDaoImplimplementsEmployeeDao {
@Override
publicEmployee findById(intempNo1) {
String sql ="select * from emp where empno = ?";
Object [] params = {empNo1};
List empList = DBUtil.executeQuery(sql,params,"com.bjsxt.entity.Employee");
//返回数据
if(empList.size()==0){
return null;
}else{
returnempList.get(0);
}
}
@Override
publicList findAll() {
String sql ="select * from emp";
Object params [] = {};
returnDBUtil.executeQuery(sql,params,Employee.class);
}
}
可以看到,提取了DBUtil的查询方法后,DAO层的查询方法代码大大简化了。这个示例其实就是在模拟数据库框架比如Hibernate、MyBatis的底层实现。
本节作业
1. ResultSetMetaData的作用和常用方法
2. 提取工具类DBUtil的查询方法,简化DAO层查询代码。
第二节 注解
2.1 认识注解
Annotation ,JDK1.5新提供的技术
我们在编程中经常会使用到注解,作用有:
1)编译检查:比如@SuppressWarnings, @Deprecated 和 @Override 都具有编译检查作用
2)替代配置文件:使用反射来读取注解信息
目前大部分框架(如Spring)都使用了注解简化代码并提高编码的效率(使用注解之前使用的xml进行配置)
注解其实就是代码里的特殊标记,它用于替代配置文件:传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。
在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。 注解可以标记在包、类、属性、方法,方法参数以及局部变量上,且同一个地方可以同时标记多个注解。
注解可以在编译(source),类加载(class),运行时(runtime)被读取,并执行相应的处理,以便于其他工具补充信息或者进行部署
2.2 内置注解
主要有三个内置注解
●@Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
●@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
●@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
从 Java 7 开始,额外添加了 3 个注解:
●@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
●@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
●@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
【示例4】认识内置注解
@SuppressWarnings(value={"all"})
public classStudent
implementsComparable, Serializable {
@Override
public intcompareTo(Student o) {//implements a method
return0;
}
@Override
publicString toString() {//override a method
return super.toString();
}
public static voidmain(String[] args) {
Date date =newDate();
System.out.println(date.toLocaleString());
Student stu =newStudent();
stu.method1();
List list =newArrayList();
}
@Deprecated
public voidmethod1(){
System.out.println("==========");
}
public voidmethod2(){
Date date =newDate();
System.out.println(date.toLocaleString());
}
}
classTestStudent{
@SuppressWarnings(value="deprecation")
public static voidmain(String[] args) {
Student stu =newStudent();
stu.method1();
}
}
2.3 元注解
元注解是指注解的注解,在JDK 1.5中提供了4个标准的用来对注解类型进行注解的注解类。可以使用这4个元注解来对我们自定义的注解类型进行注解
1. @Retention-用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime),若没有 @Retention,则默认是 RetentionPolicy.CLASS。其含有如下:
● SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)
● CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中)。
● RUNTIME:注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如SpringMvc中的@Controller、@Autowired、@RequestMapping等。
2. @Target -用来约束注解可以应用的地方(如方法、类或字段),其中ElementType是枚举类型。若没有 @Target,则该 Annotation 可以用于任何地方。
public enumElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
*@since1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
*@since1.8
*/
TYPE_USE
}
3. @Documented -标记这些注解是否包含在用户文档中。
4. @Inherited -指示注解类型被自动继承。如果在注解类型声明中存在 Inherited 元注解,并且用户在某一类声明中查询该注解类型,同时该类声明中没有此类型的注解,则将在该类的超类中自动查询该注解类型。
本节作业
1. 注解的作用
2. 内置注解及其作用
3. 元注解及其作用
第三节 注解
3.1 自定义注解
【示例5】自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(value= {ElementType.METHOD,ElementType.TYPE})
public@interfaceMyAnnoation{
intid()default0;
String name()default"";
double[] scoreArr()default{};
}
public@interfaceMyAnnotation2{
//如果只有一个配置参数,一般命名为value
String value();
}
@MyAnnotation2("bjsxt")
@MyAnnoation
public classTestAnnotation {
@MyAnnoation(id=5,name="张三",scoreArr = {78,89,34})
public static voidmain(String[] args) {
}
@MyAnnotation2(value="sxt")
public voidmethod1(){
}
}
总结:
Ø 定义注解的关键字是@interface
Ø 自定义注解中可以定义多个配置参数,不是成员方法,不是成员变量;说明参数的名称,以及参数值的类型
Ø 如果只有一个配置参数,一般命名为value
Ø 如果配置参数是value,并且只有一个配置参数,value可以省略
注意:
Ø 定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation。
Ø 和我们通常的 implements实现接口的方法不同。Annotation 接口的实现细节都由编译器完成。通过 @interface 定义注解后,该注解不能继承其他注解或接口。
Ø 注解常见的API及其关系如下
3.2 使用反射读取注解
目前大部分框架(如Spring、MyBatis、SpringMVC)都使用了注解简化代码并提高编码的效率(使用注解之前使用的xml进行配置)。
【示例6】模拟实现MyBatis的注解并使用反射读取
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
public@interfaceTable{
String value();
}
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public@interfaceColumn{
String columnName();//列名
String columnType();//列类型
intlength();//列长度
intprecision()default0;//小数位数
}
@Table(value ="t_student")
public classStudent {
@Column(columnName="id",columnType ="int",length=6)
private intid;
@Column(columnName ="sname",columnType ="varchar",length =10)
privateStringname;
@Column(columnName ="score",columnType ="double",
length =4,precision =1)
private doublescore;
}
public classTestORM {
public static voidmain(String[] args)throwsException {
String className ="com.bjsxt.annotation3.Student";
Class clazz = Class.forName(className);
//获取类的所有注解
Annotation [] annotations = clazz.getAnnotations();
for(Annotation annotation:annotations ) {
System.out.println(annotation);
}
//获取类的指定注解
Tableannotation =(Table) clazz.getAnnotation(Table.class);
System.out.println(annotation);
System.out.println(annotation.value());
//获取id属性的注解
Field idField = clazz.getDeclaredField("id");
Column idColumn =
(Column)idField.getAnnotation(Column.class);
System.out.println(idColumn.columnName());
System.out.println(idColumn.columnType());
System.out.println(idColumn.length());
System.out.println(idColumn.precision());
//获取name属性的注解
//获取score属性的注解
//拼接create DDL语句,通过JDBC创建数据库表 excuteUpdate()
//根据Student类id、name、score的值,对T_Student表进行添 //加、修改、删除操作;将T_Student表的一条记录的各列的数据取出来,存//入一个Student对象中
}
}