办公OA系统
swagger
swagger配置文件.apis(RequestHandlerSelectors.basePackage("com.travalsky.hdrd"))修改扫描包的路径。
注解
自定义注解
Java目前只内置了三种标准注解,以及四种元注解。
1、@Target
表示支持注解的程序元素的种类,注解该用于什么地方,ElementType 注解修饰的元素类型,使用ElementType枚举类来表示:
CONSTRUCTOR:构造器的声明 FIELD:域声明(包括enum实例) LOCAL_VARIABLE:局部变量声明 METHOD:方法声明 PACKAGE: 包声明 PARAMETER:参数声明 TYPE:类,接口(包括注解类型)或enum声明 ANNOTATION_TYPE:注解类型声明 TYPE_PARAMETER:类型参数声明 从jdk1.8开始 、 hide1.8 TYPE_USE:类型的使用 从jdk1.8开始 、 hide1.8
2、@Retention
表示保留时间的长短,需要在什么级别保存该注解信息, RetentionPolicy参数包括:
SOURCE:注解将被编译器丢弃。 CLASS:注解在class文件中可用,但会被VM丢弃。 RUNTIME:VM将在运行期也保留注解,因此可通过反射机制读取注解的信息。
3、@Documented
将此注解包含在Javadoc中。 表示使用该注解的元素应被javadoc或类似工具文档化,它应用于类型声明,类型声明的注解会影响客户端对注解元素的使用。如果一个类型声明添加了Documented注解,那么它的注解会成为被注解元素的公共API的一部分。
4、@Inherited
允许子类继承父类中的注解。表示一个注解类型会被自动继承,如果用户在类声明的时候查询注解类型,同时类声明中也没有这个类型的注解,那么注解类型会自动查询该类的父类,这个过程将会不停地重复,直到该类型的注解被找到为止,或是到达类结构的顶层(Object)。
通过例子验证如何使用注解
通过编写代码,更能了解注解,对注解的概念更能清楚。首先,编写一个自定义注解类BindView :
/**
* @Author lu an
* Create Date is 2019/10/9
* Des 绑定view的注解
*/
//保存的级别到运行时期
@Retention(RetentionPolicy.RUNTIME)
//目标->field 字段
@Target(ElementType.FIELD)
public @interface BindView {
int viewId() default 0;//默认0
boolean onClick() default false;//default表示默认
boolean onLongClick() default false;//default表示默认
} default表示默认值 ,也可以不编写默认值的。下面实现一个注解工具实现类:
/**
* @Author lu an
* Create Date is 2019/10/9
* Des 注解工具类
*/
public class InjectUtils {
public static void init(Activity ctx){
//获取Class对象
Class clazz = ctx.getClass();
//获取字段变量
Field[]fields = clazz.getDeclaredFields();
for (Field field : fields) {
//判断是否绑定了BindView
if(field.isAnnotationPresent(BindView.class)){
//获取到BindView对象
BindView bindView = field.getAnnotation(BindView.class);
//通过getAnnotation方法取出标记了注解的字段viewId
int viewId = bindView.viewId();
if(viewId>0){
field.setAccessible(true);
//获取到view
View childView = ctx.findViewById(viewId);
if (childView != null) {
//绑定的点击监听事件
if (bindView.onClick() && ctx instanceof View.OnClickListener) {
childView.setOnClickListener((View.OnClickListener) ctx);
}
if (bindView.onLongClick() && ctx instanceof View.OnLongClickListener) {
childView.setOnLongClickListener((View.OnLongClickListener) ctx);
}
}
}
}
}
}
} 通过反射方式,通过getAnnotation方法取出标记了注解的字段viewId,得到了viewId后,通过上下文findViewById找到对应的View控件,bindView.onClick() 对应的@BindView(viewId = R.id.btn_annotation,onClick = true)中的onClick值,ctx instanceof View.OnClickListener则表示Activity是否实现了点击接口.
下面就编写一个Activity类实现注解绑定view及其点击事件:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
//绑定view控件 和 绑定view的点击事件
@BindView(viewId = R.id.btn_annotation,onClick = true)
Button mBtnAnnotation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注解初始化
InjectUtils.init(this);
}
@Override
public void onClick(View v) {
Toast.makeText(this,"Binding View Success",Toast.LENGTH_SHORT).show();
}
} @BindView(viewId = R.id.btn_annotation,onClick = true)表示绑定view控件 和 绑定view的点击事件,如果只单单绑定view,如@BindView(viewId = R.id.btn_annotation)即可。
当你点击Button时就会弹出Binding View Success。
注解@query的使用
@query的定义
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {
// Dai Weiguo 2017/8/7 基本对象的属性名
String propName() default "";
// Dai Weiguo 2017/8/7 查询方式
Type type() default Type.EQUAL;
/**
* 多字段模糊搜索,仅支持String类型字段,多个用逗号隔开, 如@Query(blurry = "email,username")
*/
String blurry() default "";
enum Type {
// jie 2019/6/4 相等
EQUAL
// Dai Weiguo 2017/8/7 大于等于
, GREATER_THAN
// Dai Weiguo 2017/8/7 小于等于
, LESS_THAN
// Dai Weiguo 2017/8/7 中模糊查询
, INNER_LIKE
// Dai Weiguo 2017/8/7 左模糊查询
, LEFT_LIKE
// Dai Weiguo 2017/8/7 右模糊查询
, RIGHT_LIKE
// Dai Weiguo 2017/8/7 小于
, LESS_THAN_NQ
// jie 2019/6/4 包含
, IN
// 不等于
,NOT_EQUAL
// between
,BETWEEN
// 不为空
,NOT_NULL
// 查询时间
,UNIX_TIMESTAMP
}
} 注解的实现类
@Slf4j
@SuppressWarnings({"unchecked", "all"})
public class QueryHelpPlus {
public static <R, Q> QueryWrapper getPredicate(R obj, Q query) {
QueryWrapper<R> queryWrapper = new QueryWrapper<R>();
if (query == null) {
return queryWrapper;
}
try {
List<Field> fields = getAllFields(query.getClass(), new ArrayList<>());
for (Field field : fields) {
boolean accessible = field.isAccessible();
field.setAccessible(true);
Query q = field.getAnnotation(Query.class);
if (q != null) {
String propName = q.propName();
String blurry = q.blurry();
String attributeName = isBlank(propName) ? field.getName() : propName;
attributeName = humpToUnderline(attributeName);
Class<?> fieldType = field.getType();
Object val = field.get(query);
if (ObjectUtil.isNull(val) || "".equals(val)) {
continue;
}
// 模糊多字段
if (ObjectUtil.isNotEmpty(blurry)) {
String[] blurrys = blurry.split(",");
//queryWrapper.or();
queryWrapper.and(wrapper -> {
for (int i=0;i< blurrys.length;i++) {
String column = humpToUnderline(blurrys[i]);
//if(i!=0){
wrapper.or();
//}
wrapper.like(column, val.toString());
}
});
continue;
}
String finalAttributeName = attributeName;
switch (q.type()) {
case EQUAL:
//queryWrapper.and(wrapper -> wrapper.eq(finalAttributeName, val));
queryWrapper.eq(attributeName, val);
break;
case GREATER_THAN:
queryWrapper.ge(finalAttributeName, val);
break;
case LESS_THAN:
queryWrapper.le(finalAttributeName, val);
break;
case LESS_THAN_NQ:
queryWrapper.lt(finalAttributeName, val);
break;
case INNER_LIKE:
queryWrapper.like(finalAttributeName, val);
break;
case LEFT_LIKE:
queryWrapper.likeLeft(finalAttributeName, val);
break;
case RIGHT_LIKE:
queryWrapper.likeRight(finalAttributeName, val);
break;
case IN:
if (CollUtil.isNotEmpty((Collection<Long>) val)) {
queryWrapper.in(finalAttributeName, (Collection<Long>) val);
}
break;
case NOT_EQUAL:
queryWrapper.ne(finalAttributeName, val);
break;
case NOT_NULL:
queryWrapper.isNotNull(finalAttributeName);
break;
case BETWEEN:
List<Object> between = new ArrayList<>((List<Object>) val);
queryWrapper.between(finalAttributeName, between.get(0), between.get(1));
break;
case UNIX_TIMESTAMP:
List<Object> UNIX_TIMESTAMP = new ArrayList<>((List<Object>)val);
if(!UNIX_TIMESTAMP.isEmpty()){
SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long time1 = fm.parse(UNIX_TIMESTAMP.get(0).toString()).getTime()/1000;
long time2 = fm.parse(UNIX_TIMESTAMP.get(1).toString()).getTime()/1000;
queryWrapper.between(finalAttributeName, time1, time2);
}
break;
default:
break;
}
}
field.setAccessible(accessible);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return queryWrapper;
}
private static boolean isBlank(final CharSequence cs) {
int strLen;
if (cs == null || (strLen = cs.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}
return true;
}
private static List<Field> getAllFields(Class clazz, List<Field> fields) {
if (clazz != null) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
getAllFields(clazz.getSuperclass(), fields);
}
return fields;
}
/***
* 驼峰命名转为下划线命名
*
* @param para
* 驼峰命名的字符串
*/
public static String humpToUnderline(String para) {
StringBuilder sb = new StringBuilder(para);
int temp = 0;//定位
if (!para.contains("_")) {
for (int i = 0; i < para.length(); i++) {
if (Character.isUpperCase(para.charAt(i))) {
sb.insert(i + temp, "_");
temp += 1;
}
}
}
return sb.toString();
} 使用方法
业务层实现类
@Override
public List<Resume> queryAll(ResumeQueryCriteria criteria){
return baseMapper.selectList(QueryHelpPlus.getPredicate(Resume.class, criteria));
} 控制层实现类
@Log("查询简历")
@ApiOperation("查询简历/高级筛选/职位筛选")
@GetMapping
@AnonymousAccess
// @PreAuthorize("@el.check('resume:list')")
public ResponseEntity<Object> getResumes(ResumeQueryCriteria criteria, Pageable pageable){
return new ResponseEntity<>(resumeService.queryAll(criteria, pageable), HttpStatus.OK);
} 功能实现
查询
User user = userService.getById(id);//对应接口类
Evaluation evaluation = evaluationMapper.selectById(evaluationDto.getId());
增加和修改
this.saveOrUpdate(evaluation);
通用Mapper接口方法以及说明
基础接口 Select
接口:SelectMapper
方法:List select(T record);
说明:根据实体中的属性值进行查询,查询条件使用等号
接口:SelectByPrimaryKeyMapper
方法:T selectByPrimaryKey(Object key);
说明:根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
接口:SelectAllMapper
方法:List selectAll();
说明:查询全部结果,select(null)方法能达到同样的效果
接口:SelectOneMapper
方法:T selectOne(T record);
说明:根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号
接口:SelectCountMapper
方法:int selectCount(T record);
说明:根据实体中的属性查询总数,查询条件使用等号
基础接口Insert
接口:InsertMapper
方法:int insert(T record);
说明:保存一个实体,null的属性也会保存,不会使用数据库默认值
接口:InsertSelectiveMapper
方法:int insertSelective(T record);
说明:保存一个实体,null的属性不会保存,会使用数据库默认值
基础接口Update
接口:UpdateByPrimaryKeyMapper
方法:int updateByPrimaryKey(T record);
说明:根据主键更新实体全部字段,null值会被更新
接口:UpdateByPrimaryKeySelectiveMapper
方法:int updateByPrimaryKeySelective(T record);
说明:根据主键更新属性不为null的值
基础接口Delete
接口:DeleteMapper
方法:int delete(T record);
说明:根据实体属性作为条件进行删除,查询条件使用等号
接口:DeleteByPrimaryKeyMapper
方法:int deleteByPrimaryKey(Object key);
说明:根据主键字段进行删除,方法参数必须包含完整的主键属性
Base组合接口
接口:BaseSelectMapper
方法:包含上面Select的4个方法
接口:BaseInsertMapper
方法:包含上面Insert的2个方法
接口:BaseUpdateMapper
方法:包含上面Update的2个方法
接口:BaseDeleteMapper
方法:包含上面Delete的2个方法
CURD组合接口
接口:BaseMapper
方法:继承了base组合接口中的4个组合接口,包含完整的CRUD方法
Example方法
接口:SelectByExampleMapper
方法:List selectByExample(Object example);
说明:根据Example条件进行查询
重点:这个查询支持通过Example类指定查询列,通过selectProperties方法指定查询列
接口:SelectCountByExampleMapper
方法:int selectCountByExample(Object example);
说明:根据Example条件进行查询总数
接口:UpdateByExampleMapper
方法:int updateByExample(@Param(“record”) T record, @Param(“example”) Object example);
说明:根据Example条件更新实体record包含的全部属性,null值会被更新
接口:UpdateByExampleSelectiveMapper
方法:int updateByExampleSelective(@Param(“record”) T record, @Param(“example”) Object example);
说明:根据Example条件更新实体record包含的不是null的属性值
接口:DeleteByExampleMapper
方法:int deleteByExample(Object example);
说明:根据Example条件删除数据
Example组合接口
接口:ExampleMapper
方法:包含上面Example中的5个方法
Condition方法
Condition方法和Example方法作用完全一样,只是为了避免Example带来的歧义,提供的的Condition方法
接口:SelectByConditionMapper
方法:List selectByCondition(Object condition);
说明:根据Condition条件进行查询
接口:SelectCountByConditionMapper
方法:int selectCountByCondition(Object condition);
说明:根据Condition条件进行查询总数
接口:UpdateByConditionMapper
方法:int updateByCondition(@Param(“record”) T record, @Param(“example”) Object condition);
说明:根据Condition条件更新实体record包含的全部属性,null值会被更新
接口:UpdateByConditionSelectiveMapper
方法:int updateByConditionSelective(@Param(“record”) T record, @Param(“example”) Object condition);
说明:根据Condition条件更新实体record包含的不是null的属性值
接口:DeleteByConditionMapper
方法:int deleteByCondition(Object condition);
说明:根据Condition条件删除数据
Condition组合接口
接口:ConditionMapper
方法:包含上面Condition中的5个方法
RowBounds
默认为内存分页,可以配合PageHelper实现物理分页
接口:SelectRowBoundsMapper
方法:List selectByRowBounds(T record, RowBounds rowBounds);
说明:根据实体属性和RowBounds进行分页查询
接口:SelectByExampleRowBoundsMapper
方法:List selectByExampleAndRowBounds(Object example, RowBounds rowBounds);
说明:根据example条件和RowBounds进行分页查询
接口:SelectByConditionRowBoundsMapper
方法:List selectByConditionAndRowBounds(Object condition, RowBounds rowBounds);
说明:根据example条件和RowBounds进行分页查询,该方法和selectByExampleAndRowBounds完全一样,只是名字改成了Condition
RowBounds组合接口
接口:RowBoundsMapper
方法:包含上面RowBounds中的前两个方法,不包含selectByConditionAndRowBounds
Special特殊接口
这些接口针对部分数据库设计,不是所有数据库都支持
接口:InsertListMapper
方法:int insertList(List recordList);
说明:批量插入,支持批量插入的数据库可以使用,例如MySQL,H2等,另外该接口限制实体包含id属性并且必须为自增列
接口:InsertUseGeneratedKeysMapper
方法:int insertUseGeneratedKeys(T record);
说明:插入数据,限制为实体包含id属性并且必须为自增列,实体配置的主键策略无效
MySQL专用
接口:MySqlMapper
继承方法:int insertList(List recordList);
继承方法:int insertUseGeneratedKeys(T record);
说明:该接口不包含方法,继承了special中的InsertListMapper和InsertUseGeneratedKeysMapper
SQLServer专用
由于sqlserver中插入自增主键时,不能使用null插入,不能在insert语句中出现id。
注意SqlServer的两个特有插入方法都使用了
@Options(useGeneratedKeys = true, keyProperty = “id”)
这就要求表的主键为id,且为自增,如果主键不叫id可以看高级教程中的解决方法。
另外这俩方法和base中的插入方法重名,不能同时存在!
如果某种数据库和SqlServer这里类似,也可以使用这些接口(需要先测试)。
经测试,PostgreSQL可以采用。
接口:InsertMapper
方法:int insert(T record);
说明:插入数据库,null值也会插入,不会使用列的默认值
接口:InsertSelectiveMapper
方法:int insertSelective(T record);
说明:插入数据库,null的属性不会保存,会使用数据库默认值
接口:SqlServerMapper
说明:这是上面两个接口的组合接口。
Mapper接口
接口:Mapper
该接口兼容Mapper2.x版本,继承了BaseMapper, ExampleMapper, RowBoundsMapper三个组合接口。

京公网安备 11010502036488号