Method to create a custom Java annotation class

  • 2020-04-01 03:58:24
  • OfStack

  If you already use Java programming and any popular frameworks like Spring and Hibernate, you should be familiar with the use of annotations. When working with an existing framework, its annotations are usually sufficient. However, do you sometimes have a need to create your own annotations?

Not long ago, I found a reason to create my own annotation, a project that involved validating commonly used data stored in a variety of databases.
Scene description

The business had multiple databases that held the same data and had different ways to keep it up to date, and the business had planned to consolidate all of this data into a single master database to reduce the complexity of the problems involved with multiple data sources.


But before the start of the project, the business also need to know how much gap distance can synchronize data, and make any necessary correction to make its can be synchronized. The first step you need to create a general data reports show the data of database, and to validate its value, for those who do not conform to the conditions of the records highlighted. There is a demand for the brief summary:

      Compare data from common parts of multiple databases, such as customer, company, or directory information.       The default value should match all databases according to the value type.       For some fields, we just want to show the value, and we don't want to do any data comparisons.       For some fields, we just want to compare their values and validate the data at the specified particular data source.       For some fields, we might want to do some complex data comparisons, perhaps based on other fields in the record.       For some fields, we might want to format the data in a particular format, such as $000,000.00 for the number of COINS.       The report should be in MS Excel format, with each row containing field values from each data source. Any rows that do not match the data validation rules should be highlighted in yellow.

annotations

After a scrutiny of the requirements, and some ideas for a while, I decided to use annotations to driver for data comparison and report processing configuration. What we need is simple, and highly flexible and extensible. These comments will be the field level, and I don't like configuration is hidden somewhere in the classpath file. So, you will be able to see the same fields directly associated annotations, in order to know specific is how to deal with it.

In the simplest case, the annotation is a sign, not just provide information to the operation of the code itself has a direct effect of meta data. If you have been engaged in Java programming, so now you use of them should be quite familiar with, but you may never need to create your own notes. To do this, you need to create a new type with a Java type @ interface, it will contain detailed information to specify metadata elements.

Here's an example from this project:
 


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReconField {
 
  
  boolean compareSources() default true;
 
  
  ReconDisplayFormat displayFormat() default ReconDisplayFormat.NATIVE;
 
  
  String id();
 
  
  String label() default "";
 
  
  ReconSource[] sourcesToCompare() default {};
 
}

This is how the operation of the main drive data matching process. It contains the basic elements of, can meet the demand of most of the data comparison between different data sources. @ ReconField can handle in addition to more complex than we had expected, most of the demand for more complex situations we will discuss in later. Most of these elements in the list of code are introduced in a one-on-one annotation, and it should be pointed out that, there are several key on our @ ReconField annotations.

      This annotation lets you specify which Java element your annotation should be used on. The possible Target types are ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, and TYPE. In our @reconfield annotation it is specified to the FIELD level.

      Possible values are CLASS, RUNTIME, and SOURCE. Because we're going to handle the annotation at RUNTIME, that's what we need to set.


The data validation process will run a query for each database, and to map the results to show for a specific business record types in all fields of the entity bean. Mapping data entity annotation on each field will tell how the processors for specific fields and perform data found in each database of its value. So let's look at a few examples to understand how these annotations are used in different configuration of data comparison.

In order to validate the existing values and match them exactly only in each data source, you only need to provide a field ID and the markup that will be displayed on the report.


 
@ReconField(id = CUSTOMER_ID, label = "Customer ID")
private String customerId;

To show the values found in each data source without any data comparison, you may need to specify the compareSources element and set its value to false.
 


@ReconField(id = NAME, label = "NAME", compareSources = false)
private String name;


To test found in the specified data source of value, but not all, you could use to elementsourcesToCompare. Use this thing will show all the values found, but only to find the values of the elements listed in the data sources. So it can deal with some not all the data stored in each data source scene. ReconSource is a contains the enumerated types can be used to compare the data source.
 


@ReconField(id = PRIVATE_PLACEMENT_FLAG, label = "PRIVATE PLACEMENT FLAG", sourcesToCompare ={ ReconSource.LEGACY, ReconSource.PACE })
private String privatePlacementFlag;

Now that we have met our basic requirements, we need to solve the problem of implementing the ability to specify fields for complex data comparisons; to do this, we will create a second annotation to drive custom rule processing.
 


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReconCustomRule {
 

String[] params() default {};
 

Class<?> processor() default DefaultReconRule.class;
 
}


Annotations are very similar with before, the biggest difference is that @ ReconCustomRule we specify a class in the annotations, it can be executed when the reconstruction processing data. You can define only will be used in class, so your processor can instantiate and initialize the class you specify. The specified in the annotation class will need to implement a general rule interface, it will be rule processor used to execute the rules.

Now let's look at an example of using this annotation.

In this case, we use a custom rule that will check if the stock exchange is United States and if so, skip this data; to do this, the rule will need to check the exchange country field in the same record.
 


@ReconField(id = STREET_CUSIP, label = "STREET CUSIP", compareSources = false)
@ReconCustomRule(processor = SkipNonUSExchangeComparisonRule.class)
private String streetCusip;

Example here we specify a parameter for custom rules, here it is a package capacity. For this kind of special data comparison, by comparing the values of the not deviate more than 1000. By using the capacity of parameter specifies the package, we can use different package capacity will be on the same set of custom rules applied to more fields. The only drawback is that due to the nature of the annotations, these parameters can be static, so can't dynamic changes.
 


@ReconField(id = USD_MKT_CAP, label = "MARKET CAP USD", displayFormat = ReconDisplayFormat.NUMERIC_WHOLE, sourcesToCompare =
{ ReconSource.LEGACY, ReconSource.PACE, ReconSource.BOB_PRCM })
@ReconCustomRule(processor = ToleranceAmountRule.class, params = { "10000" })
private BigDecimal usdMktCap;

As you can see, we only use a few simple annotation, it designed a considerable degree of flexibility to multiple database scenario data validation report function. In this particular case, the annotation is driving the data comparison process, so we are actually using the annotations on the mapping data entities find calculations and use them directly for processing.


Related articles: