项目收获-多表查询的sql语句的拼接+反射

在项目中,涉及到了多表查询,总共有6张表,在前端页面中显示的输入框的又多,获得的参数不一定是有值的,语句拼接非常麻烦。


在编写项目代码的时候,我们已经给各个实体类写好了一一对应的dao,为了拼接sql语句,我也写好了单个对象拼接sql语句的工具方法。

在多表多条件查询的时候,我们之前的做法是从第一个类开始,查出符合该类约束的所有对象,然后通过已知的对象集合,查出与之相关的数据(约束条件=类本事约束条件+已知外数据的外键约束),这样到了最后,得到了要查找的数据。

再根据查的的数据往回约束查找,也找到其他类数据。

这样做虽然要求是达到了,但是感觉根本没有效率可言,最好的方法当然是用一条语句来查询出来,然后再把返回数据分成多个对象来返回。


现在就想办法来设计一个通用的多表查询的方法:

返回值:

平常我们返回的应该是List<Object>类型的数据,现在我们要返回的是多个这样的List,那么这里就用Map<Class,List<Object>>用类对象去取其中的集合。

参数:

要传入查询的条件,那就要传入 数据库中列的名字,列的值,还有表与表之间的联系。列的名字可以查找映射配置文件来得到,所以可以选择传入类中属性的名字,表与表之间的联系只要知道了类也可以通过配置文件获取,那也不传了。也就是说要传的是属性的名字与属性的值,再封装一下,直接传对象,完事。


现在方法的大概样子应该是这样的

public Map<Class, List<Object>> multiClassQuery(Object[] objs){

}

对应的sql语句应该是:

select * from t1 [(join t2 on ...) (join t3 on ...)...] [ where conditions];

所以说第一个表与其他的表还是有点不一样的,所以将方法改成这样:

public Map<Class, List<Object>> multiClassQuery(Object firstObj,Object... objs){
}

具体实现:

sql语句应该拆成2部分

1.select * from t1后面来一个表添加一个 “join tx on (...)”

2.where 后面一部分,来一个表,填一些有的限制

其他:

为了防止sql注入,还是使用"?"占位符,将实际的值存到List中,到时候再set到PreparedStatment中


结果处理:

最后结果返回的应该是几行数据,数据的列数是所有类属性数之和。

每一个ResultSet.nex()对应的一大行数据,每一列的通过ResultSet.get(index++)来获取,并且放到对象里去。


------------------------------------------------------------------------------------------------------------

代码部分:

上面提到了配置信息,这里了用这个封装类来表示某个类与数据库映射的的信息。

public class DbMapping {
	private Class clazz;

	private DbMapping(Class clazz) {
		this.clazz = clazz;
	}

	private Map<Class, Map<String, String>> wholeMap = new LinkedHashMap<Class, Map<String, String>>();

	public String getForeignKey() {
		return "";
	}
	public String getPrimaryKey(){
		return "";
	}
	public Map<String, String> getMapping() {
		return wholeMap.get(clazz);
	}

	public String getTableName() {
		return wholeMap.get(clazz).get(clazz.getSimpleName());
	}

	public String getColumnMapping(Field field) {
		return wholeMap.get(clazz).get(field.getName());
	}

	public static DbMapping obtain(Class clazz) {
		return new DbMapping(clazz);
	}

}
这里的实例化用obtain()方法获取对象,在后面改动的时候可以少产生对象。


接着是这个方法:

public Map<Class, List<Object>> multiClassQuery(Object firstObj,
			Object... objs) throws Exception {
		checkArguments(firstObj,objs);
		// 这个用来粗放返回值
		Map<Class, List<Object>> container = new HashMap<Class, List<Object>>();

		// 以下三行用来存放生成的params与sql语句
		List<Object> params = new ArrayList<Object>();
		StringBuffer headSql = new StringBuffer("select * from ");
		StringBuffer tailSql = new StringBuffer(" where ");

		// //以下是处理第一个对象
		// 获取映射分装类
		DbMapping dbMapping = DbMapping.obtain(firstObj.getClass());
		// 得到表名,生成sql
		String tableName = dbMapping.getTableName();
		headSql.append(tableName);
		// 将对象中的属性添加到sql中的约束部分,并且添加params
		Field[] fields = firstObj.getClass().getDeclaredFields();
		for (Field field : fields) {
			field.setAccessible(true);
			Object fieldValue = field.get(firstObj);
			// 那就说明这个属性需要转化成sql语句
			if (fieldValue != null || !"".equals(fieldValue)) {
				String columnName = dbMapping.getColumnMapping(field);
				tailSql.append(columnName).append("=? and ");
				params.add(fieldValue);
			}
		}
		List<Object> firstValues = new ArrayList<Object>();
		container.put(firstObj.getClass(), firstValues);
		// //第一个对象处理完毕

		// //---下面处理后面的对象
		for (Object obj : objs) {
			String oldTableName = tableName;
			String oldPrimaryKey = dbMapping.getPrimaryKey();
			dbMapping = DbMapping.obtain(obj.getClass());
			// 先处理headSql
			// 这里要把自己表名加上,还要把表与表之间的联系加上
			tableName = dbMapping.getTableName();
			String newForeignKey = dbMapping.getForeignKey();
			headSql.append(" join ").append(tableName).append(" on ") //
					.append(oldTableName).append(".").append(oldPrimaryKey) //
					.append("=") //
					.append(tableName).append(".").append(newForeignKey);

			// 再来第二段sql语句
			fields = obj.getClass().getDeclaredFields();
			for (Field field : fields) {
				field.setAccessible(true);
				Object fieldValue = field.get(firstObj);
				if (fieldValue != null || !"".equals(fieldValue)) {
					String columnName = dbMapping.getColumnMapping(field);
					tailSql.append(columnName).append("=? and ");
					params.add(fieldValue);
				}
			}

			List<Object> values = new ArrayList<Object>();
			container.put(obj.getClass(), values);
		}

		String finalSql = headSql.append(
				tailSql.delete(tailSql.length() - 4, tailSql.length() - 1))
				.toString();
		System.out.println("sql = " + finalSql);
		System.out.println("params = " + params);

		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = ConnectionPool.getConnection();
			ps = conn.prepareStatement(finalSql);
			for (int i = 0; i < params.size(); i++) {
				ps.setObject(i + 1, params.get(i));
			}
			rs = ps.executeQuery();
			while (rs.next()) {
				int rsCursor = 1;
				Object instance = firstObj.getClass().newInstance();
				fields = firstObj.getClass().getDeclaredFields();
				for (Field field : fields) {
					field.setAccessible(true);
					field.set(instance, rs.getObject(rsCursor++));
				}
				container.get(firstObj.getClass()).add(instance);

				for (Object obj : objs) {
					instance = obj.getClass().newInstance();
					fields = obj.getClass().getDeclaredFields();
					for (Field field : fields) {
						field.setAccessible(true);
						field.set(instance, rs.getObject(rsCursor++));
					}
					container.get(obj.getClass()).add(instance);
				}
			}
		} finally {
			rs.close();
			ps.close();
			conn.close();
		}

		return null;
	}
	/**
	 * 检查参数异常,抛出。
	 * */
	private void checkArguments(Object firstObj, Object[] objs) {
		if(firstObj==null){
			throw new RuntimeException();
		}
	}
这就写完了,至于可行性,下次我再试试。。。。。。。。。。。



这里用到了好多次的getDeclaredFields()方法,本来听说反射耗时,而且是在for循环里使用,所以我就很担心效率会变得特别差。

于是就做了测试:

public static void main(String[] args) throws InterruptedException {
		System.out.println(System.currentTimeMillis());
		Field[] fields = PersonalBase.class.getDeclaredFields();
		System.out.println(System.currentTimeMillis());
		Thread.sleep(10000);
		System.out.println(System.currentTimeMillis());
		for (int i = 0; i < 100; i++) {
			fields = PersonalBase.class.getDeclaredFields();

		}
		System.out.println(System.currentTimeMillis());
	}
结果是这样的:

1436091716807
1436091716809
1436091726810
1436091726812
看来也就第一次耗时,后面的肯定是做了处理,本来想做个代理缓存了,现在看来也不用了

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java通过反射注解生成对应的SQL语句的步骤如下: 1. 定义注解:首先需要定义一个注解,该注解用于标识字段信息,例如字段名、字段类型、是否为主键等信息。 ``` @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Column { String name(); // 字段名 String type(); // 字段类型 boolean primaryKey() default false; // 是否为主键 } ``` 2. 定义实体类:在实体类的字段上使用上面定义的注解。 ``` public class User { @Column(name = "id", type = "int", primaryKey = true) private int id; @Column(name = "name", type = "varchar") private String name; @Column(name = "age", type = "int") private int age; // 省略getter和setter方法 } ``` 3. 通过反射获取实体类的字段信息:通过反射获取实体类的字段信息,然后读取字段上的注解,获取字段名、字段类型和是否为主键等信息。 ``` public static <T> String generateCreateTableSql(Class<T> clazz) { StringBuilder sb = new StringBuilder(); sb.append("CREATE TABLE IF NOT EXISTS "); sb.append(clazz.getSimpleName()); sb.append("("); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Column.class)) { Column column = field.getAnnotation(Column.class); sb.append(column.name()); sb.append(" "); sb.append(column.type()); if (column.primaryKey()) { sb.append(" PRIMARY KEY"); } sb.append(","); } } sb.deleteCharAt(sb.length() - 1); sb.append(")"); return sb.toString(); } ``` 4. 生成SQL语句:将读取到的字段信息拼接成对应的SQL语句,例如创建表、插入数据、更新数据等。 ``` public static <T> String generateInsertSql(T entity) { StringBuilder sb = new StringBuilder(); sb.append("INSERT INTO "); sb.append(entity.getClass().getSimpleName()); sb.append("("); Field[] fields = entity.getClass().getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Column.class)) { Column column = field.getAnnotation(Column.class); sb.append(column.name()); sb.append(","); } } sb.deleteCharAt(sb.length() - 1); sb.append(") VALUES ("); for (Field field : fields) { if (field.isAnnotationPresent(Column.class)) { field.setAccessible(true); Object value = field.get(entity); if (value instanceof String) { sb.append("'"); sb.append(value); sb.append("'"); } else { sb.append(value); } sb.append(","); } } sb.deleteCharAt(sb.length() - 1); sb.append(")"); return sb.toString(); } ``` 以上就是利用反射注解生成对应的SQL语句的步骤,可以根据需要进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值