Java游戏服务器开发之十三--整合MyBatis通用语句工具

这次的修改内容
1.为了将一些可复用的方法统一使用,修改源码MapperAnnotationBuilder
2.MapperAnnotationBuilder中使用到的内容,包括CrudMapper/CrudProvider/CrudSqlProvider/CrudSqlSource
3.当使用缓存的时候,需要实现Serializable,添加基类Entity
4.dao实现CrudMapper,实体类实现Entity
5.xml文件中添加实体类的基类<property name="typeAliasesSuperType" value="com.lizhaoblog.base.mybatis.Entity"></property>
6.TestFirstHandler中添加测试方法      
   项目变化如图所示


使用一些现有比较完善的mybatis小工具,可以不用再写一些通用的方法(增删改查)
通过修改org.apache.ibatis.builder.annotation.MapperAnnotationBuilder中的方法
在getSqlSourceFromAnnotations中修改
			else {
                CrudProvider crudAnnotation = method
                        .getAnnotation(CrudProvider.class);
                if (crudAnnotation != null) {
                    Type[] genericInterfaces = this.type.getGenericInterfaces();
                    if (genericInterfaces.length > 0) {
                        ParameterizedType parameterizedType = (ParameterizedType) genericInterfaces[0];
                        Class<?> entityType = (Class<?>) parameterizedType
                                .getActualTypeArguments()[0];
                        return new CrudSqlSource(assistant.getConfiguration(),
                                crudAnnotation, entityType, method);
                    }
                }
            }
			
在getSqlCommandType中修改
	Class<? extends Annotation> type = getSqlAnnotationType(method);
        if (type == null) {
            type = getSqlProviderAnnotationType(method);

            if (type == null) {
                if (method.isAnnotationPresent(CrudProvider.class)) {
                    String sqlType = method.getAnnotation(CrudProvider.class).sqlType();
                    return SqlCommandType.valueOf(sqlType.toUpperCase());
                }
                return SqlCommandType.UNKNOWN;


在方法中需要用到2个类(CrudProvider/CrudSqlSource),这个通过我们自己去实现,下面都是工具类,可以直接使用
CrudMapper 封装dao层通用的增删改查方法
CrudProvider 对数据库的操作类型,注解
CrudSqlProvider 写的通用方法的sql提供者,@see CrudMapper
CrudSqlSource 从XML文件或注释读取的映射语句的内容,创建sql
Entity 实体类对应的接口
EntityUtils 实体类操作工具
   
   具体代码

    CrudMapper

/*
 * Copyright (C), 2015-2018
 * FileName: CrudMapper
 * Author:   zhao
 * Date:     2018/7/4 10:33
 * Description: 封装dao层通用的增删改查方法
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.base.mybatis;

import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.ResultType;

import java.io.Serializable;
import java.util.List;

/**
 * 〈一句话功能简述〉<br>
 * 〈封装dao层通用的增删改查方法〉
 *
 * @author zhao
 * @date 2018/7/4 10:33
 * @since 1.0.0
 */
public interface CrudMapper<T, ID extends Serializable>  {

  @CrudProvider
  @ResultMap("resultMap")
  T findOne(ID id);

  @CrudProvider
  @ResultType(Boolean.class)
  @Options(useCache = false)
  boolean exists(ID id);

  @CrudProvider
  @ResultMap("resultMap")
  @Options(useCache = false)
  List<T> listAll();

  @CrudProvider
  @ResultType(Long.class)
  @Options(useCache = false)
  Long count();

  @CrudProvider
  @ResultMap("resultMap")
  @Options(useCache = false)
  List<T> listByExample(T example);

  @CrudProvider
  @ResultType(Long.class)
  @Options(useCache = false)
  Long countByExample(T example);

  @CrudProvider(sqlType = "delete")
  @Options(flushCache = true)
  void deleteById(ID id);

  @CrudProvider(sqlType = "delete")
  @Options(flushCache = true)
  void deleteByIds(List<ID> ids);

  @CrudProvider(sqlType = "delete")
  @Options(flushCache = true)
  void deleteByIdArray(ID[] ids);

  @CrudProvider(sqlType = "delete")
  @Options(flushCache = true)
  void delete(T entity);

  @CrudProvider(sqlType = "delete")
  @Options(flushCache = true)
  void deleteEntities(List<T> entities);

  @CrudProvider(sqlType = "insert")
  @Options(flushCache = true)
  void insert(T entity);

  @CrudProvider(sqlType = "update")
  @Options(flushCache = true)
  void update(T entity);


}

CrudProvider

/*
 * Copyright (C), 2015-2018
 * FileName: CrudProvider
 * Author:   zhao
 * Date:     2018/7/4 10:38
 * Description: 对数据库的操作类型,注解
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.base.mybatis;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 〈一句话功能简述〉<br>
 * 〈对数据库的操作类型,注解〉
 *
 * @author zhao
 * @date 2018/7/4 10:38
 * @since 1.0.0
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CrudProvider {
  Class<?> type() default CrudSqlProvider.class;

  String sqlType() default "select";

  String method() default "";

  boolean isJpaStyle() default false;

}

CrudSqlProvider
/*
 * Copyright (C), 2015-2018
 * FileName: CrudSqlProvider
 * Author:   zhao
 * Date:     2018/7/4 10:39
 * Description: 提供生成sql语句
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.base.mybatis;

import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.jdbc.SQL;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * 〈一句话功能简述〉<br>
 * 〈写的通用方法的sql提供者,@see CrudMapper〉
 *
 * @author zhao
 * @date 2018/7/4 10:39
 * @since 1.0.0
 */
public class CrudSqlProvider {
  private Class<?> entityType;
  private String tableName;
  private String idColumnName;
  private String idPropertyName;

  public CrudSqlProvider(Class<?> entityType) {
    this.entityType = entityType;
    initTableName();
    initIdColumnName();
    initIdPropertyName();
  }

  public String findOne() {
    return selectFrom().WHERE(eqId()).toString();
  }

  public String exists() {
    return countFrom().WHERE(eqId()).toString();
  }

  public String listAll() {
    return selectFrom().toString();
  }

  public String count() {
    return countFrom().toString();
  }

  public String listByExample(Object example) throws IllegalAccessException {
    SQL sql = selectFrom();
    appendConditionByExample(sql, example);
    return sql.toString();
  }

  public String countByExample(Object example) throws IllegalAccessException {
    SQL sql = countFrom();
    appendConditionByExample(sql, example);
    return sql.toString();
  }

  public String deleteById() {
    return deleteFrom().WHERE(eqId()).toString();
  }

  public String deleteByIds(List<Object> ids) {
    SQL sql = deleteFrom();
    if (CollectionUtils.isEmpty(ids)) {
      throw new BuilderException("参数ids不能为空");
    }
    StringBuilder sb = new StringBuilder(idColumnName).append(" in (");
    for (Object id : ids) {
      if (id instanceof String) {
        sb.append("'").append(id).append("'");
      } else {
        sb.append(id);
      }
      sb.append(",");
    }
    sb.deleteCharAt(sb.length() - 1);
    sb.append(")");
    sql.WHERE(sb.toString());
    return sql.toString();
  }

  public String deleteByIdArray(Object[] ids) {
    return deleteByIds(Arrays.asList(ids));
  }

  public String delete() {
    return deleteFrom().WHERE(eqId()).toString();
  }

  public String deleteEntities(List<Object> entities)
          throws NoSuchFieldException, SecurityException, IllegalAccessException {
    if (CollectionUtils.isEmpty(entities)) {
      throw new BuilderException("参数entities不能为空");
    }
    List<Object> ids = new ArrayList<Object>();
    for (Object entity : entities) {
      Field idField = entityType.getDeclaredField(idPropertyName);
      idField.setAccessible(true);
      ids.add(idField.get(entity));
    }
    return deleteByIds(ids);
  }

  public String deleteAll() {
    return deleteFrom().toString();
  }

  public String insert(Object entity) throws IllegalAccessException {
    SQL sql = new SQL().INSERT_INTO(tableName);
    Field[] fields = entityType.getDeclaredFields();
    for (Field field : fields) {
      if (field.isAnnotationPresent(Id.class) && field.isAnnotationPresent(GeneratedValue.class)) {
        continue;
      }
      field.setAccessible(true);
      Object fieldVal = field.get(entity);
      Column colomnAnnotation = field.getAnnotation(Column.class);
      boolean bool = StringUtils.isEmpty(Objects.toString(fieldVal, "")) && (colomnAnnotation != null || field
              .isAnnotationPresent(Id.class));
      if (!bool) {
        String propertyName = field.getName();
        String columnName = propertyName;
        if (colomnAnnotation != null) {
          columnName = colomnAnnotation.name();
          if (StringUtils.isEmpty(columnName)) {
            columnName = propertyName;
          }
        }
        sql.VALUES(columnName, wrapParameter(propertyName));
      }
    }
    return sql.toString();
  }

  public String update(Object entity) throws IllegalAccessException {
    SQL sql = new SQL().UPDATE(tableName);
    Field[] fields = entityType.getDeclaredFields();
    for (Field field : fields) {
      if (field.isAnnotationPresent(Id.class)) {
        continue;
      }
      field.setAccessible(true);
      Object fieldVal = field.get(entity);
      Column colomnAnnotation = field.getAnnotation(Column.class);
      if (!StringUtils.isEmpty(Objects.toString(fieldVal, "")) && colomnAnnotation != null) {
        String propertyName = field.getName();
        String columnName = colomnAnnotation.name();
        if (StringUtils.isEmpty(columnName)) {
          columnName = propertyName;
        }
        sql.SET(assign(columnName, propertyName));
      }
    }
    return sql.WHERE(eqId()).toString();
  }

  private SQL deleteFrom() {
    return new SQL().DELETE_FROM(tableName);
  }

  public String findWithJpaStyle(String methodName) {
    // TODO support jpastyle
    SQL sql = selectFrom();
    return sql.toString();
  }

  private SQL selectFrom() {
    return new SQL().SELECT("*").FROM(tableName);
  }

  public String countWithJpaStyle(String methodName) {
    // TODO support jpastyle
    SQL sql = countFrom();
    return sql.toString();
  }

  private SQL countFrom() {
    return new SQL().SELECT("count(*)").FROM(tableName);
  }

  private void appendConditionByExample(SQL sql, Object example) throws IllegalAccessException {
    Field[] fields = entityType.getDeclaredFields();
    for (Field field : fields) {
      field.setAccessible(true);
      Object fieldVal = field.get(example);
      Column colomnAnnotation = field.getAnnotation(Column.class);
      if (!StringUtils.isEmpty(Objects.toString(fieldVal, "")) && colomnAnnotation != null) {
        String propertyName = field.getName();
        String columnName = colomnAnnotation.name();
        if (StringUtils.isEmpty(columnName)) {
          columnName = propertyName;
        }
        sql.WHERE(eq(columnName, propertyName));
      }
    }
  }

  private String eqId() {
    return eq(idColumnName, idPropertyName);
  }

  private String eq(String columnName, String propertyName) {
    return String.format("%s = #{%s}", columnName, propertyName);
  }

  private String assign(String columnName, String propertyName) {
    return String.format("%s = #{%s}", columnName, propertyName);
  }

  private String wrapParameter(String propertyName) {
    return String.format("#{%s}", propertyName);
  }

  private void initIdColumnName() {
    Field[] fields = entityType.getDeclaredFields();
    for (Field field : fields) {
      if (field.isAnnotationPresent(Id.class) && field.isAnnotationPresent(Column.class)) {
        idColumnName = field.getAnnotation(Column.class).name();
        if (StringUtils.isEmpty(idColumnName)) {
          idColumnName = field.getName();
        }
      } else if (field.isAnnotationPresent(Id.class)) {
        idColumnName = field.getName();
      }
    }
    if (StringUtils.isEmpty(idColumnName)) {
      throw new IllegalArgumentException("实体未注解Id");
    }
  }

  private void initIdPropertyName() {
    Field[] fields = entityType.getDeclaredFields();
    for (Field field : fields) {
      if (field.isAnnotationPresent(Id.class)) {
        idPropertyName = field.getName();
      }
    }
    if (StringUtils.isEmpty(idPropertyName)) {
      throw new IllegalArgumentException("实体未注解Id");
    }
  }

  private void initTableName() {
    Table tableAnno = entityType.getAnnotation(Table.class);
    if (tableAnno == null) {
      throw new IllegalArgumentException("实体未注解Table");
    }
    tableName = tableAnno.name();
    if (StringUtils.isEmpty(tableName)) {
      tableName = getTableNameFromClass(entityType);
    }
  }

  private String getTableNameFromClass(Class<?> clazz) {
    String simpleName = clazz.getSimpleName();
    String first = simpleName.substring(0, 1);
    String remains = clazz.getSimpleName().substring(1);
    return first + remains;
  }

}


CrudSqlSource
/*
 * Copyright (C), 2015-2018
 * FileName: CrudSqlSource
 * Author:   zhao
 * Date:     2018/7/4 10:46
 * Description: sql
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.base.mybatis;

import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.defaults.DefaultSqlSession;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.HashMap;

/**
 * 〈一句话功能简述〉<br>
 * 〈SqlSource:从XML文件或注释读取的映射语句的内容。
 * 创建将从用户接收的输入参数中传递到数据库的SQL。〉
 *
 * @author zhao
 * @date 2018/7/4 10:46
 * @since 1.0.0
 */
public class CrudSqlSource implements SqlSource {
  private SqlSourceBuilder sqlSourceParser;
  private Class<?> providerType;
  private Method providerMethod;
  private boolean providerTakesParameterObject;
  private Class<?> entityType;
  private boolean isJpaStyle = false;
  private Method mapperInterfaceMethod;

  public CrudSqlSource(Configuration config, CrudProvider provider, Class<?> entityType, Method mapperInterfaceMethod) {
    String providerMethodName = null;
    try {
      this.entityType = entityType;
      this.mapperInterfaceMethod = mapperInterfaceMethod;
      this.sqlSourceParser = new SqlSourceBuilder(config);
      this.providerType = provider.type();
      isJpaStyle = provider.isJpaStyle();
      providerMethodName = provider.method();
      if (StringUtils.isEmpty(providerMethodName)) {
        providerMethodName = mapperInterfaceMethod.getName();
      }
      if (isJpaStyle) {
        if (providerMethodName.startsWith("find")) {
          providerMethodName = "findWithJpaStyle";
        } else if (providerMethodName.startsWith("count")) {
          providerMethodName = "countWithJpaStyle";
        } else if (providerMethodName.startsWith("delete")) {
          providerMethodName = "deleteWithJpaStyle";
        } else {
          throw new BuilderException("only method startwith find/count/delete support jpa style");
        }
      }
      for (Method m : this.providerType.getMethods()) {
        if (providerMethodName.equals(m.getName())) {
          if (m.getParameterTypes().length < 2 && m.getReturnType() == String.class) {
            this.providerMethod = m;
            this.providerTakesParameterObject = m.getParameterTypes().length == 1;
          }
        }
      }
    } catch (Exception e) {
      throw new BuilderException("Error creating SqlSource for SqlProvider.  Cause: " + e, e);
    }
    if (this.providerMethod == null) {
      throw new BuilderException(
              "Error creating SqlSource for SqlProvider. Method '" + providerMethodName + "' not found in SqlProvider '"
                      + this.providerType.getName() + "'.");
    }
  }

  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    SqlSource sqlSource = createSqlSource(parameterObject);
    return sqlSource.getBoundSql(parameterObject);
  }

  private SqlSource createSqlSource(Object parameterObject) {
    try {
      String sql;
      Object providerInstance = providerType.getConstructor(Class.class).newInstance(entityType);
      if (isJpaStyle) {
        sql = (String) providerMethod.invoke(providerInstance, mapperInterfaceMethod.getName());
      } else {
        if (providerTakesParameterObject) {
          if (parameterObject instanceof DefaultSqlSession.StrictMap) {
            DefaultSqlSession.StrictMap strictMap = (DefaultSqlSession.StrictMap) parameterObject;
            parameterObject = strictMap.get(strictMap.keySet().iterator().next());
          }
          sql = (String) providerMethod.invoke(providerInstance, parameterObject);
        } else {
          sql = (String) providerMethod.invoke(providerInstance);
        }
      }

      Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
      return sqlSourceParser.parse(sql, parameterType, new HashMap<String, Object>());
    } catch (Exception e) {
      throw new BuilderException(
              "Error invoking SqlProvider method (" + providerType.getName() + "." + providerMethod.getName()
                      + ").  Cause: " + e, e);
    }
  }

}


Entity
/*
 * Copyright (C), 2015-2018
 * FileName: Entity
 * Author:   zhao
 * Date:     2018/7/4 10:52
 * Description: 实体类对应的接口
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.base.mybatis;

import java.io.Serializable;

/**
 * 〈一句话功能简述〉<br>
 * 〈实体类对应的接口〉
 *
 * @author zhao
 * @date 2018/7/4 10:52
 * @since 1.0.0
 */
public interface Entity extends Serializable {

}


EntityUtils
/*
 * Copyright (C), 2015-2018
 * FileName: EntityUtils
 * Author:   zhao
 * Date:     2018/7/4 10:53
 * Description: 实体类操作工具
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.base.mybatis;

import org.springframework.util.StringUtils;

import java.lang.reflect.Field;

import javax.persistence.Column;
import javax.persistence.Id;

/**
 * 〈一句话功能简述〉<br>
 * 〈实体类操作工具〉
 *
 * @author zhao
 * @date 2018/7/4 10:53
 * @since 1.0.0
 */
public final class EntityUtils {
  public static String getColumnName(Class<?> entityClass, String fieldName)
          throws NoSuchFieldException, SecurityException {
    String columnName = null;
    Field field = entityClass.getDeclaredField(fieldName);
    if (field.isAnnotationPresent(Column.class)) {
      columnName = field.getAnnotation(Column.class).name();
      if (StringUtils.isEmpty(columnName)) {
        columnName = fieldName;
      }
    } else if (field.isAnnotationPresent(Id.class)) {
      columnName = fieldName;
    }
    if (StringUtils.isEmpty(columnName)) {
      throw new IllegalArgumentException("实体未注解");
    }
    return columnName;
  }

  private EntityUtils() {

  }
}


   
这几个变化也没什么好贴代码的了
4.然后修改dao实现CrudMapper,实体类实现Entity
5.xml文件中添加实体类的基类<property name="typeAliasesSuperType" value="com.lizhaoblog.base.mybatis.Entity"></property>

6.TestFirstHandler中添加测试方法

	List<User> all = userDao.listAll();
    logger.info(all.toString());

    User user = new User();
    user.setName("a");
    user.setAccount("aa");
    user.setPassword("aaa");
    userDao.insert(user);

    all = userDao.listAll();
    logger.info(all.toString());


  上面的代码在码云上 https://gitee.com/lizhaoandroid/JgServer
可以加qq群一起探讨Java游戏服务器开发的相关知识 676231524


      

猜你喜欢

转载自blog.csdn.net/cmqwan/article/details/80910361