前言:
注解作用:
注解,告诉编译器如何运行程序
注解和XML配置的区别:
传统方式通过配置文件告诉类如何运行,有了注解技术后,
开发人员可以通过注解告诉类如何运行。
在Java技术里注解的典型应用是:
可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
注解作用:
1. 告诉编译器如何运行程序;
2. 简化(取代)配置文件
常用注解:
// 重写父类的方法
1. @Override
// 抑制编译器警告
2. @SuppressWarnings({"unused","unchecked"})
// 标记方法以及过时
3. @Deprecated
自定义注解格式:
public @interface Author {
/**
* 注解属性
* 1. 修饰为默认或public
* 2. 不能有主体
*/
String name();
int age() default 30; // 带默认值的注解; 使用的时候就可以不写此属性值
}
public @interface Author {
// 如果注解名称为value,使用时候可以省略名称,直接给值
// (且注解只有一个属性时候才可以省略名称)
String value();
}
使用
@Author("Jet")
@Author(value = "Jet")
注解属性类型为数组:
public @interface Author {
String[] value() default {"test1","test2"};
}
使用:
@Author({“”,“”})
public void save() {
}
元注解
元注解,表示注解的注解!
指定注解的可用范围:
@Target({
TYPE, 类
FIELD, 字段
METHOD, 方法
PARAMETER, 参数
CONSTRUCTOR, 构造器
LOCAL_VARIABLE 局部变量
})
元注解 - 2. 指定注解的声明周期
@Retention(RetentionPolicy.SOURCE) 注解只在源码级别有效
@Retention(RetentionPolicy.CLASS) 注解在字节码即别有效 默认值
@Retention(RetentionPolicy.RUNTIME) 注解在运行时期有效
前一章知道了泛型也可以使用反射:
父类通用类使用反射得到运行类的类型
但是有缺陷,因为 实体类名和数据库表名要一致,属性也要和字段一模一样才行。
可是事实上不是这样的:
当表名与数据库名称不一致、
字段与属性不一样、
主键不叫id,
解决方案1:
可以通过配置文件(XML) 解决!
类似于:配置类名=表名 属性名=字段名
解决方案2:
使用注解!
1. 自定义注解 使得属性对应列名,还有类名对应表名
2. 再设置个无属性的注解,标识主键对应的属性
3. 使用元注解设置生命周期为运行环境
4. 使用元注解设置注解范围
先看看两者区别:
注解:
简化XML配置, 程序处理非常方便!
(不便于维护: 例如修改字段名,要重新编译!)
XML
便于维护! 需要些读取代码!
两者区别:
传统方式通过配置文件告诉类如何运行,有了注解技术后,
开发人员可以通过注解告诉类如何运行。
在Java技术里注解的典型应用是:
可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
接下来看看优化之前的BasicDao通用类:
JdbcUtilsC3p0就不多介绍了
实体类:
@Table(tableName = "students")
public class Students {
@id
@Column(columnName = "id")
private int student_id;
@Column(columnName = "name")
private String student_name;
@Column(columnName = "age")
private int student_age;
@Override
public String toString() {
// TODO Auto-generated method stub
return student_id + ":" + student_name + ":" + student_age;
}
}
自定义注解:
1. Table注解:
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String tableName();
}
id注解:
import static java.lang.annotation.ElementType.FIELD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface id {
}
Column注解:
import static java.lang.annotation.ElementType.FIELD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String columnName();
}
子类:
public class StudentDao extends BasicDao<Students>{
}
通用父类:
public class BasicDao<T> {
private Class<T> clazz;
private String tableName;
private String id_primary;
public BasicDao() {
Type type = this.getClass().getGenericSuperclass(); // BasicDao<Students>
// 强转参数化类型
ParameterizedType pType = (ParameterizedType) type;
Type[] types = pType.getActualTypeArguments();
// 获得参数化类型的实参:Students
clazz = (Class<T>) types[0];
// 获得注解
Table table = clazz.getAnnotation(Table.class);
// 得到该对象对应的数据库表名称
tableName = table.tableName();
// 获得该类的属性数组
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 可以强制访问class的private类型的属性
field.setAccessible(true);
id id_a = field.getAnnotation(id.class);
if (id_a != null) {
Column column = field.getAnnotation(Column.class);
id_primary = column.columnName();
break;
}
}
System.out.println("表名:" + tableName);
System.out.println("主键:" + id_primary);
}
public T findById(int i) {
String sql = "SELECT *FROM " + tableName + " WHERE " + id_primary + "=?";
try {
return JdbcUtilsC3p0.getQueryRunner().query(sql, new MyBeanHandler<T>(clazz), i);
} catch (Exception e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
}
public List<T> finAll() {
String sql = "SELECT *FROM " + tableName ;
try {
return JdbcUtilsC3p0.getQueryRunner().query(sql, new MyBeanListHandler<T>(clazz));
} catch (Exception e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
}
}
class MyBeanHandler<T> implements ResultSetHandler<T> {
private Class<T> clazz;
public MyBeanHandler(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public T handle(ResultSet arg0) throws SQLException {
try {
T c = clazz.newInstance();
if (arg0.next()) {
// 获取类的所有的Field字段数组
Field[] fields = clazz.getDeclaredFields();
// 其中还有个$jacocoData
System.out.println(fields.length);
for (Field field : fields) {
if (!"$jacocoData".equals(field.getName())) {
// 获取属性名
String fieldName = field.getName();
// 获得属性上的注解
Column column = field.getAnnotation(Column.class);
// 获得属性对应的数据库表的字段
String name = column.columnName();
// 得到字段值
Object value = arg0.getObject(name);
BeanUtils.copyProperty(c, fieldName, value);
}
}
}
return c;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class MyBeanListHandler<T> implements ResultSetHandler<List<T>>{
private Class<T> clazz;
public MyBeanListHandler(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public List<T> handle(ResultSet arg0) throws SQLException {
try {
List<T> list = new ArrayList<T>();
while(arg0.next()) {
T c = clazz.newInstance();
// 获取类的所有的Field字段数组
Field[] fields = clazz.getDeclaredFields();
// 其中还有个$jacocoData
System.out.println(fields.length);
for (Field field : fields) {
if (!"$jacocoData".equals(field.getName())) {
// 获取属性名
String fieldName = field.getName();
// 获得属性上的注解
Column column = field.getAnnotation(Column.class);
// 获得属性对应的数据库表的字段
String name = column.columnName();
// 得到字段值
Object value = arg0.getObject(name);
BeanUtils.copyProperty(c, fieldName, value);
}
}
list.add(c);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
主程序入口:
public static void main(String[] args) throws Exception {
StudentDao studentDao = new StudentDao();
Students students = studentDao.findById(1);
System.out.println(students);
System.out.println("__________________________________________");
List<Students> list = new ArrayList<Students>();
list= studentDao.finAll();
for(Students s:list) {
System.out.println(s);
}
}
输出:
表名:students
主键:id
8月 08, 2018 9:46:34 下午 com.mchange.v2.log.MLog
信息: MLog clients using java 1.4+ standard logging.
8月 08, 2018 9:46:34 下午 com.mchange.v2.c3p0.C3P0Registry
信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
8月 08, 2018 9:46:35 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1hge4n59x75lm5msdkh6h|44c73c26, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.cj.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hge4n59x75lm5msdkh6h|44c73c26, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/daynow?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 1000, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 6, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {password=******, user=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
4
1:我:12
__________________________________________
4
4
1:我:12
2:你:33