0811KQC—注解反射—orm框架
0811KQC
@MyEmpAnno(id=1,name="张三",sex="男",age=16,method = "introduce")
public class Student {
private Integer id;
private String name;
private String sex;
private Integer age;
public Student() {
}
public Student(Integer id, String name, String sex, Integer age) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
}
public void introduce(){
System.out.println("id:"+id+"name:"+name+"sex:"+sex+"age"+age);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyEmpAnno {
int id(); //姓名
String name(); //性别
String sex(); //年龄
String method(); //定义将要执行的方法
int age();
}
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
Class cls = Class.forName("Student");
Annotation[] as = cls.getAnnotations();
for (Annotation annotation : as){
if(annotation instanceof MyEmpAnno){
MyEmpAnno myEmpAnno = (MyEmpAnno) annotation;
// int id(); //编号
// String name(); //名字
// String sex(); //性别
// int age(); //年龄
// String method(); //定义将要执行的方法
int id = myEmpAnno.id();
String name = myEmpAnno.name();
String sex = myEmpAnno.sex();
String method = myEmpAnno.method();
int age = myEmpAnno.age();
Object obj = cls.newInstance();
Field f1= cls.getDeclaredField("id");
f1.setAccessible(true);
f1.set(obj,id);
Field f2 = cls.getDeclaredField("name");
f2.setAccessible(true);
f2.set(obj,name);
Field f3 = cls.getDeclaredField("sex");
f3.setAccessible(true);
f3.set(obj,sex);
Field f4 = cls.getDeclaredField("age");
f4.setAccessible(true);
f4.set(obj,age);
System.out.println(obj);
//调用指定方法
Method m = cls.getDeclaredMethod(method);
m.invoke(obj);
break;
}
}
}
}
1.使用反射获取注解,设置对象值
Student:
package com.test2;
@MyStuAnno(value="aaa",name="张三",sex="男",nums = {6,7,8})
public class Student {
private Integer studentId;
private String name;
private String sex;
private Integer age;
public Student() {
}
public Student(Integer studentId, String name, String sex, Integer age) {
this.studentId = studentId;
this.name = name;
this.sex = sex;
this.age = age;
}
public Integer getStudentId() {
return studentId;
}
public void setStudentId(Integer studentId) {
this.studentId = studentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"studentId=" + studentId +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}
MyStuAnno:
package com.test2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyStuAnno {
//定义注解的姓名属性
//当一个注解属性没有默认值,并且不是value属性的时候,使用的时候必须带上属性名赋值
//String name();
//default:可以为属性设置默认值
//String name() default "张三";
String name();
String sex();
//value:是一个特殊的属性,设置该属性的时候,可以省略名字,
// 但是和其他属性一起使用的时候,也要加属性名
String value() default "";
//注解中的数组属性
int[] nums() default {1,2,3,4};
}
2.orm框架:实现数据的增删改查
创建数据库:
#创建数据库
create database 70811_db
#设置字符集
default character set utf8mb4
#设置编码方式
default collate utf8mb4_general_ci
创建数据表:
#创建表
create table emp
(
empId int primary key auto_increment,
name varchar(20),
sex varchar(20),
birthday date
);
select * from emp;
insert into emp
(name,sex,birthday)
select '张三','男','2000-01-01' union
select '李四','女','2002-01-01';
drop table orders;
create table orders
(
orderId int primary key auto_increment,
customerId int,
reciverName varchar(20),
reciverAddress varchar(200),
recivarPhone varchar(11),
money decimal(5,1),
orderTime datetime
);
select * from orders;
insert into orders
(customerId,reciverName,reciverAddress,recivarPhone,money,orderTime)
select 1,'张三','河南','12312312311',10,now() union
select 2,'李四','河北','12312312312',20,now() union
select 3,'王五','山东','12312312313',30,now();
代码:
整体布局:
util:
BaseDAO:
package orm.util;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.*;
import java.util.*;
public class BaseDAO {
//四大金刚
//驱动类
private static final String DRIVER="com.mysql.cj.jdbc.Driver";
//连接地址
private static final String URL="jdbc:mysql://localhost:3306/70811_db?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
//用户名
private static final String USER="root";
//密码
private static final String PASSWORD="123456";
//获取连接
public static Connection getConnection(){
Connection con = null;
try{
//加载驱动类
Class.forName(DRIVER);
//获取连接
con = DriverManager.getConnection(URL,USER,PASSWORD);
}catch(Exception ex){
ex.printStackTrace();
}
return con;
}
//关闭数据库对象
public static void closeAll(Connection con,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
if(st!=null){
try{
st.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
if(con!=null){
try{
con.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
}
//通用设置参数方法
public static void setParams(PreparedStatement pst,Object[] params){
if(params==null){
return;
}
for(int i=0;i<params.length;i++){
try{
pst.setObject(i+1,params[i]);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
//通用增删改
public static int executeUpdate(String sql,Object[] params){
Connection con = null;
PreparedStatement pst = null;
int res = -1;
try{
//获取连接
con = getConnection();
//创建预编译命令执行对象
pst = con.prepareStatement(sql);
//设置参数
setParams(pst,params);
//执行
res = pst.executeUpdate();
}catch(Exception ex){
ex.printStackTrace();
}finally{
closeAll(con,pst,null);
}
return res;
}
//获取总记录数的查询:select count(*) from ..
public static int getTotal(String sql,Object[] params){
int total = 0;
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
try{
con = getConnection();
pst = con.prepareStatement(sql);
setParams(pst,params);
rs = pst.executeQuery();
//判断是否查询除了一个记录
if(rs.next()){
total = rs.getInt(1);
}
}catch (Exception ex){
ex.printStackTrace();
}finally {
closeAll(con,pst,rs);
}
return total;
}
//通用查询
public static List<Map<String,Object>> executeQuery(String sql,Object[] params) {
List<Map<String,Object>> rows = new ArrayList<>();
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
try{
//获取连接
con = getConnection();
//获取命令对象
pst = con.prepareStatement(sql);
//设置参数
setParams(pst,params);
//执行查询
rs = pst.executeQuery();
//通过rs获取结果集的结构信息
ResultSetMetaData rsmd = rs.getMetaData();
//获取结果集的列数
int colCount = rsmd.getColumnCount();
//遍历查询结果,并封装到List<Map>中
while(rs.next()){
//用Map存储当前行的各个列数据
Map<String,Object> map = new HashMap<>();
//循环获取每一列的信息
for(int i=1;i<=colCount;i++){
//获取列名(使用rsmd)
String colName = rsmd.getColumnLabel(i);
//获取列值(使用rs)
Object colVal = rs.getObject(i);
//将当前列存储到map中
map.put(colName,colVal);
}
//将遍历的当前行的数据存储到List中
rows.add(map);
}
}catch(Exception ex){
ex.printStackTrace();
}finally{
closeAll(con,pst,rs);
}
return rows;
}
}
entity:
Emp:
package orm.entity;
import orm.anno.Table;
import orm.anno.TableColumn;
import orm.anno.TableId;
import java.util.Date;
@Table("emp")
public class Emp {
//标记该列是主键列,并且是自增的
@TableId(isAutoGen = true)
//标记该属性对应数据表的中列的信息
@TableColumn(colName = "empId",colType = "int",colSize = 11)
private Integer empId;
@TableColumn(colName = "name",colType = "varchar",colSize = 20)
private String name;
@TableColumn(colName = "sex",colType = "char",colSize = 1)
private String sex;
@TableColumn(colName = "birthday",colType = "date",colSize = 8)
private Date birthday;
public Emp() {
}
public Emp(Integer empId, String name, String sex, Date birthday) {
this.empId = empId;
this.name = name;
this.sex = sex;
this.birthday = birthday;
}
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Emp{" +
"empId=" + empId +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
'}';
}
}
Orders :
package orm.entity;
import orm.anno.Table;
import orm.anno.TableColumn;
import orm.anno.TableId;
import java.util.Date;
@Table("orders")
public class Orders {
@TableId(isAutoGen = true)
@TableColumn(colName = "orderId",colType = "int",colSize = 11)
private Integer orderId;
@TableColumn(colName = "customerId",colType = "int",colSize = 11)
private Integer customerId;
private String reciverName;
private String reciverAddress;
private String reciverPhone;
private Double money;
private Date orderTime;
public Orders() {
}
public Orders(Integer orderId, Integer customerId, String reciverName, String reciverAddress, String reciverPhone, Double money, Date orderTime) {
this.orderId = orderId;
this.customerId = customerId;
this.reciverName = reciverName;
this.reciverAddress = reciverAddress;
this.reciverPhone = reciverPhone;
this.money = money;
this.orderTime = orderTime;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public Integer getCustomerId() {
return customerId;
}
public void setCustomerId(Integer customerId) {
this.customerId = customerId;
}
public String getReciverName() {
return reciverName;
}
public void setReciverName(String reciverName) {
this.reciverName = reciverName;
}
public String getReciverAddress() {
return reciverAddress;
}
public void setReciverAddress(String reciverAddress) {
this.reciverAddress = reciverAddress;
}
public String getReciverPhone() {
return reciverPhone;
}
public void setReciverPhone(String reciverPhone) {
this.reciverPhone = reciverPhone;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public Date getOrderTime() {
return orderTime;
}
public void setOrderTime(Date orderTime) {
this.orderTime = orderTime;
}
@Override
public String toString() {
return "Orders{" +
"orderId=" + orderId +
", customerId=" + customerId +
", reciverName='" + reciverName + '\'' +
", reciverAddress='" + reciverAddress + '\'' +
", reciverPhone='" + reciverPhone + '\'' +
", money=" + money +
", orderTime=" + orderTime +
'}';
}
}
dao:
IGenericDAO:
package orm.dao;
import java.util.List;
import java.util.Map;
public interface IGenericDAO<T> {
List<Map<String,Object>> listAll(Class<T> clz);
int insert(T t);
int update(T t);
int delete(Integer id);
}
GenericDAOImpl:
package orm.dao.impl;
import orm.anno.Table;
import orm.anno.TableColumn;
import orm.dao.IGenericDAO;
import orm.dao.old.IEmpDAO;
import orm.util.BaseDAO;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
/**
* @author: hy
* @create: 2022-08-11 10:28:41
*/
public class GenericDAOImpl<T> implements IGenericDAO<T> {
//通过反射获取拼接的sql
public String getQuerySql(Class<T> clz){
//定义sql
StringBuilder sql = new StringBuilder();
//使用clz通过反射,获取对应类型上标记的注解(类的注解@Table,字段注解@TableCoumn)
String tableName = "";
if(clz.isAnnotationPresent(Table.class)){
Table t = clz.getDeclaredAnnotation(Table.class);
//获取注解上定义的表名
tableName = t.value();
StringBuilder cols = new StringBuilder();
//获取该实体类下的所有字段
Field[] fields = clz.getDeclaredFields();
//遍历字段,读取字段上的注解
for (Field f : fields){
//判断字段上有没有TableColumn注解
if(f.isAnnotationPresent(TableColumn.class)){
//获取字段上注解
TableColumn tc = f.getDeclaredAnnotation(TableColumn.class);
//获取该注解定义的列名
String colName = tc.colName();
//拼接列名
cols.append(colName+",");
}
}
//拼接查询的列名,结构如下: empId,name,sex,birthday
String colsRes = cols.substring(0,cols.length()-1);
//拼接完整的sql
sql.append("select ").append(colsRes).append(" from ").append(tableName);
}
return sql.toString();
}
//查找
@Override
public List<Map<String, Object>> listAll(Class<T> clz) {
String sql = getQuerySql(clz);
return BaseDAO.executeQuery(sql,null);
}
//通过反射获取拼接的sql
public String getInsertSql(Class<T> clz){
//定义sql
StringBuilder sql = new StringBuilder();
//使用clz通过反射,获取对应类型上标记的注解(类的注解@Table,字段注解@TableCoumn)
String tableName = "";
if(clz.isAnnotationPresent(Table.class)){
Table t = clz.getDeclaredAnnotation(Table.class);
//获取注解上定义的表名
tableName = t.value();
StringBuilder cols = new StringBuilder();
//获取该实体类下的所有字段
Field[] fields = clz.getDeclaredFields();
//遍历字段,读取字段上的注解
for (Field f : fields){
//判断字段上有TableColumn注解,且没有TableId注释
if(f.isAnnotationPresent(TableColumn.class) && !f.isAnnotationPresent(TableId.class)){
//获取字段上注解
TableColumn tc = f.getDeclaredAnnotation(TableColumn.class);
//获取该注解定义的列名
String colName = tc.colName();
//拼接列名
cols.append(colName+",");
}
}
//拼接查询的列名,结构如下: name,sex,birthday
String colsRes = cols.substring(0,cols.length()-1);
//拼接完整的sql
sql.append("insert into ").append(tableName).append("(").append(colsRes).append(")").append(" values ").append(" (?,?,?) ");
}
System.out.println("sql.toString():"+sql.toString());
return sql.toString();
}
//插入
@Override
public int insert(T t) {
List<Object> list=new ArrayList<>();
//定义sql
Class itCls = t.getClass();
String sql = getInsertSql(itCls);
Field[] fields=itCls.getDeclaredFields();
for(Field f:fields){
try {
if(f.isAnnotationPresent(TableColumn.class) && !f.isAnnotationPresent(TableId.class)){
f.setAccessible(true);
list.add(f.get(t));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
System.out.println("list.toArray()2:"+list.toArray());
return BaseDAO.executeUpdate(sql,list.toArray());
}
//通过反射获取拼接的sql
public String getUpdateSql(Class<T> clz){
//定义sql
StringBuilder sql = new StringBuilder();
//使用clz通过反射,获取对应类型上标记的注解(类的注解@Table,字段注解@TableCoumn)
String tableName = "";
if(clz.isAnnotationPresent(Table.class)){
Table t = clz.getDeclaredAnnotation(Table.class);
//获取注解上定义的表名
tableName = t.value();
StringBuilder cols = new StringBuilder(); //name,sex,birthday
StringBuilder cols1 = new StringBuilder(); // empId
//获取该实体类下的所有字段
Field[] fields = clz.getDeclaredFields();
//遍历字段,读取字段上的注解
for (Field f : fields){
//判断字段上有TableColumn注解,且没有TableId注释
if(f.isAnnotationPresent(TableColumn.class) && !f.isAnnotationPresent(TableId.class)){
//获取字段上注解
TableColumn tc = f.getDeclaredAnnotation(TableColumn.class);
//获取该注解定义的列名
String colName = tc.colName();
//拼接列名
cols.append(colName+"=?,");
}
//判断字段上有没有TableId注释
if(f.isAnnotationPresent(TableColumn.class) && f.isAnnotationPresent(TableId.class)){
//获取字段上注解
TableColumn tc = f.getDeclaredAnnotation(TableColumn.class);
//获取该注解定义的列名
String colName = tc.colName();
//拼接列名
cols1.append(colName+"=?");
}
}
//拼接查询的列名,结构如下: name,sex,birthday empId
String colsRes = cols.substring(0,cols.length()-1);
//拼接完整的sql
// update goods
// set price = 0.1
// where goodsId = 1111
sql.append("update ").append(tableName).append(" set ").append(colsRes).append(" where ").append(cols1);
}
System.out.println("sql.toString():"+sql.toString());
return sql.toString();
}
//更改
@Override
public int update(T t) {
List list=new ArrayList();
Object b = null; //局部变量需要赋初值
Class uecls=t.getClass();
String sql=getUpdateSql(uecls);
Field[] fields=uecls.getDeclaredFields();
for(Field f:fields){
try {
if(f.isAnnotationPresent(TableColumn.class) && !f.isAnnotationPresent(TableId.class)){
f.setAccessible(true);
System.out.println(f.get(t));
list.add(f.get(t));
}
if(f.isAnnotationPresent(TableColumn.class) && f.isAnnotationPresent(TableId.class)){
f.setAccessible(true);
b=f.get(t);
//System.out.println(f.get(t));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// for(Field f:fields){
// try {
// if(f.isAnnotationPresent(TableColumn.class) && f.isAnnotationPresent(TableId.class)){
// f.setAccessible(true);
// list.add(f.get(t));
// System.out.println(f.get(t));
// }
//
// } catch (IllegalAccessException e) {
// e.printStackTrace();
// }
//
// }
list.add(b);
System.out.println("list.toArray()3:"+ Arrays.toString(list.toArray()));
return BaseDAO.executeUpdate(sql,list.toArray());
}
//删除
@Override
public int delete(Integer id) {
String sql=" delete from emp " +
" where empId =?";
Object[] params={id};
return BaseDAO.executeUpdate(sql,params);
}
}
old:
IEmpDAO:
package orm.dao.old;
import orm.entity.Emp;
import java.util.List;
import java.util.Map;
public interface IEmpDAO {
List<Map<String,Object>> listAll();
int insert(Emp emp);
int update(Emp emp);
int delete(Integer empId);
}
IOrdersDAO:
package orm.dao.old;
import orm.entity.Emp;
import orm.entity.Orders;
import java.util.List;
import java.util.Map;
public interface IOrdersDAO {
List<Map<String,Object>> listAll();
int insert(Orders orders);
int update(Orders orders);
int delete(Integer orderId);
}
EmpDAOImpl:
package orm.dao.old.impl;
import orm.dao.old.IEmpDAO;
import orm.entity.Emp;
import java.util.List;
import java.util.Map;
/**
* @author: hy
* @create: 2022-08-11 10:24:36
*/
public class EmpDAOImpl implements IEmpDAO {
@Override
public List<Map<String, Object>> listAll() {
return null;
}
@Override
public int insert(Emp emp) {
return 0;
}
@Override
public int update(Emp emp) {
return 0;
}
@Override
public int delete(Integer empId) {
return 0;
}
}
OrdersDAOImpl:
package orm.dao.old.impl;
import orm.dao.old.IOrdersDAO;
import orm.entity.Orders;
import java.util.List;
import java.util.Map;
/**
* @author: hy
* @create: 2022-08-11 10:24:56
*/
public class OrdersDAOImpl implements IOrdersDAO {
@Override
public List<Map<String, Object>> listAll() {
return null;
}
@Override
public int insert(Orders orders) {
return 0;
}
@Override
public int update(Orders orders) {
return 0;
}
@Override
public int delete(Integer orderId) {
return 0;
}
}
anno:
Table :
package orm.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table { //标记表名
//设置实体对应的数据库表名
String value();
}
TableColumn:
package orm.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableColumn { //标记普通列
//列名
String colName();
//列的类型
String colType();
//列的长度
int colSize();
}
TableId:
package orm.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: hy
* @create: 2022-08-11 10:33:51
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableId { //用于定义主键列
//是否自增
boolean isAutoGen();
}
test:
Test:
package orm.test;
import orm.dao.IGenericDAO;
import orm.dao.impl.GenericDAOImpl;
import orm.entity.Emp;
import orm.entity.Orders;
import java.sql.Date;
import java.util.List;
import java.util.Map;
import static java.util.Date.*;
public class Test {
public static void main(String[] args) {
// IEmpDAO empDAO = new EmpDAOImpl();
// empDAO.listAll();
IGenericDAO<Emp> dao = new GenericDAOImpl<Emp>();
//查找
List<Map<String,Object>> empList = dao.listAll(Emp.class);
System.out.println(empList);
//添加
// dao.insert(new Emp());
Emp emp=new Emp("赵六","男", Date.valueOf("1990-09-09"));
int it=dao.insert(emp);
if(it==1){
System.out.println("插入成功!");
}
//更新
// dao.update(new Emp());
Emp emp1=new Emp(2,"刘七","男", Date.valueOf("1998-09-09"));
int ue=dao.update(emp1);
if(ue==1){
System.out.println("修改成功!");
}
//删除
// dao.delete(10);
int de=dao.delete(1);
if(de==1){
System.out.println("删除成功!");
}
//查询orders (orders的增删改查和emp的思路是相同的。直接拿来用即可。删除的时候需要修改一下列名。略)
IGenericDAO<Orders> dao2 = new GenericDAOImpl<>();
List<Map<String,Object>> ordersList = dao2.listAll(Orders.class);
System.out.println(ordersList);
// dao2.insert(new Orders());
// dao2.update(new Orders());
// dao2.delete(1);
}
}
运行结果如下:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/118069.html