In the company's actual project, there is a program that has a lot of configuration in the T_config table of the database, which makes it easy for the operator to modify these configurations through the front page.
The early developers did not cache the table, and each transaction was read in real time to the corresponding configuration in the database.
As the volume of business continues to increase, the discovery of lower performance, after the investigation is found to be too frequent read the database,
Because the company temporarily does not access Redis technology, so we wrote some cache and overload caching methods, these frequently read database information cached in memory, speed up reading speed, reduce database operation
Here is a brief introduction to this scenario, first of all, the table structure
CREATE TABLE ' t_config ' (
' ID ' int () NOT NULL auto_increment,
' NAME ' varchar ' is not NULL,
' DATA ' varchar (2 Default NULL,
' remark ' varchar (255) default NULL,
PRIMARY KEY (' ID ')
The corresponding JavaBean
public class Tconfig implements Serializable, cloneable {
private static final long Serialversionuid = 161981168413879 1L;
Private Long ID;
private String name;
Private String data;
Private String remark;
..................
the mapping public
static class Tconfigmapper implements Rowmapper<tconfig> {
for Get set and constructor//jdbctemplate is omitted Public Tconfig Maprow (ResultSet rs, int index) throws SQLException {tconfig
tconfig = new Tconfig ();
Long id = rs.getlong ("id");
Tconfig.setid (ID);
String name = rs.getstring ("name");
Tconfig.setname (name);
String data = rs.getstring ("data");
Tconfig.setdata (data);
String Remark = rs.getstring ("remark");
Tconfig.setremark (remark);
return tconfig;
}
}
The project uses a mysql,jre version of 1.7,spring4.3.4, and ORM uses Spring's jdbctemplate, the data source is C3P0
Next, the note, which is simple, has only two fields, name is name in the database, and does not specify the name of the current property that is implied
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 Tconfigcache {
String name () default "";
/**
* overload interval, the unit is minutes, the default 5 minutes, the minimum is 1 minutes
*
*
@return
/int intervaltime () default 5;
}
Let's take a look at these test data stored in the database
Next is Configsfromdb, all of the database configuration is in this abstract class, this class just declare the variable, and specify the Get method, do not care about how the variable is assigned and timed overload, note the value of the image and the database data
Public abstract class Configsfromdb {
@TConfigCache
private String is_send;
@TConfigCache
private int start_time;//supports basic data types
@TConfigCache
private Integer end_time;
@TConfigCache (name = "Threshold_amount", IntervalTime = 1)
private Double qidianjine;//Start amount
Private String String Normal properties, not annotated
/**
* After loading data callback, can do some of the cleanup work, but not the necessary
* *
protected void Afterload () {
if ( Qidianjine = = null) {
qidianjine = 0.0
;
}
} /**
* After loading data is recalled, do some data validation check, but not required
* @return
/protected Boolean checkdata () {
if (start_ Time >= end_time) {return
false;
}
return true;
}
Public String Getis_send () {return
is_send;
}
Public Integer Getstart_time () {return
start_time;
}
Public Integer Getend_time () {return
end_time;
}
Public Double Getqidianjine () {return
qidianjine;
}
public string getString () {return
string;
}
}
Next is the play, Configsfromdbimpl, inherits the Configsfromdb, uses to identify Tconfigcache annotations, and assigns values to these attributes, timed overloads, etc.
Import Java.lang.reflect.Field;
Import java.util.ArrayList;
Import Java.util.HashMap;
Import java.util.List;
Import Java.util.Map;
Import javax.annotation.PostConstruct;
Import org.springframework.beans.factory.annotation.Autowired;
Import Org.springframework.jdbc.core.JdbcTemplate;
Import org.springframework.scheduling.annotation.Scheduled;
Import Org.springframework.stereotype.Repository; @Repository ("Configs") public class Configsfromdbimpl extends Configsfromdb {@Autowired private jdbctemplate Jdbctempla
Te
Private list<myfield> fields = new arraylist<myfield> ();
Private final String SQL = "SELECT * from T_config";
/** * Basic Data type boxing class name conversion/final private static map<string, string> Type_map = new hashmap<string, string> ();
static {type_map.put ("int", Integer.class.getSimpleName ());
Type_map.put ("Double", Double.class.getSimpleName ());
Type_map.put ("Short", Short.class.getSimpleName ()); Type_map.put ("Long", Long.class.getSimpleName ());
Type_map.put ("Byte", Byte.class.getSimpleName ());
Type_map.put ("float", Float.class.getSimpleName ());
Type_map.put ("Boolean", Boolean.class.getSimpleName ());
Type_map.put ("Char", Character.class.getSimpleName ());
@PostConstruct private void Init () throws Exception {Initanno ();
if (!load ()) {///throws Exception, the spring initialization fails and the program fails to start throw new Exception ("Initialization configsfromdb failure, program cannot start");
} private Boolean Load () throws Exception {//Load data, and assign System.out.println ("Start Loading Database Configuration table");
Long starttime = System.currenttimemillis (); list<tconfig> configs = jdbctemplate.query (sql, New Tconfig.tconfigmapper ())//execute SQL, full table query int length = Fields.siz
E ();
for (int i = 0; i < length; i++) {MyField MyField = Fields.get (i);
Field field = Myfield.field;
Tconfigcache anno = field.getdeclaredannotation (Tconfigcache.class);
String name = Field.getname (); if (! "".
Equals (Anno.name ())) {//If the name of the annotation is not empty, take the name of the annotation = Anno.name (); } if (Istimeout (MyfiEld, Anno.intervaltime ()) {//To determine if timeout for (Tconfig config:configs) {if (Config.getname (). Equals (name)) { SetValue (field, Config.getdata ()); Assign a value to a property MyField = new MyField (field, System.currenttimemillis ()); Re-setting time Fields.set (I, MyField); Put back the original position Configs.remove (config);
Removed from the current database query results to reduce the number of subsequent traversal System.out.println (Field.getname () + "Get the database value of" +config.getdata ());
Break
}} else {System.out.println (Field.getname () + "Property not timed out, no overload required");
Continue } afterload ();
Callback Data post-processing method SYSTEM.OUT.PRINTLN ("Load Database configuration table end, time consuming:" + (System.currenttimemillis ()-starttime)); return Checkdata (); Callback data checking method} private void Initanno () {//is looking for a property for Tconfig annotation (Field field:this.getClass (). Getsuperclass (). getdecl
Aredfields ()) {field.setaccessible (true);
if (Field.isannotationpresent (Tconfigcache.class)) {//Judge is Tconfigcache annotation MyField MyField = new MyField (field, 0);
Fields.Add (MyField); System.out.prinTLN ("successfully identified to" + field.getname () + "attribute annotated"); /** * Assigns a value to a property, and can do the corresponding String conversion based on the type of the different attribute/private void setValue (Field field, String value) throws Illegalacce
ssexception {String type = Field.gettype (). Getsimplename ();
if (Field.gettype (). Isprimitive ()) {//Judge whether it is a basic data type such as Int,long, then obtain its boxed type = Type_map.get (type);
Switch (type) {//note jdk1.7 above only supports this syntax case "Integer": Field.set (this, integer.parseint (value));
Break
Case "Double": Field.set (this, double.parsedouble (value));
Break
Case ' short ': Field.set (this, short.parseshort (value));
Break
Case "Long": Field.set (this, Long.parselong (value));
Break
Case "Byte": Field.set (this, byte.parsebyte (value));
Break
Case "Character": Field.set (this, value);
Break Case "Boolean": Field.set (this, Boolean.parseboolean (value));
For this conversion, you can add additional customizations, such as 1 or on to represent the true break;
You can continue to add a conversion with string type, such as date, BigInteger, Default:field.set (this, value); }}/** * judgmentDoes this attribute timeout * * @param myField * @return/private Boolean istimeout (MyField myField, int intervaltime) {long L
Astloadtime = Myfield.lastloadtime;
if (System.currenttimemillis () < (lastloadtime + intervaltime * 1000)) {return false;
return true;
@SuppressWarnings ("unused") private class MyField {//field is simply encapsulated so that it has the "last Load Time" property field field;
Long lastloadtime = 0;
Public MyField (Field field, Long Lastloadtime) {super ();
This.field = field;
This.lastloadtime = Lastloadtime;
Public MyField (Field field) {super ();
This.field = field;
Public MyField () {super (); }/** * Timed load Data * @throws Exception/@Scheduled (fixeddelay = 1 * 1000, InitialDelay = 2 * 60 * 1000)//
1 minutes timed up, initial set up dormant 2 minutes private void Loadbytime () throws Exception {System.out.println ("@Scheduled Start overloading Data");
Load ();
System.out.println ("@Scheduled Start overloading Data completion"); }
}
Finally, use the demo
Import Org.springframework.context.ApplicationContext;
Import Org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {public
static void Main (string[] args) throws Throwable {
ApplicationContext applicationconte XT = new Classpathxmlapplicationcontext ("./config/applicationcontext.xml");
Final Configsfromdb configs = (configsfromdb) applicationcontext.getbean ("configs");
The new Thread () {//timed print cache data public
void run () {while
(true) {
System.out.println ("-----------------");
System.out.println (Configs.getis_send ());
System.out.println (Configs.getstart_time ());
System.out.println (Configs.getend_time ());
System.out.println (Configs.getqidianjine ());
System.out.println ("-----------------");
try {Sleep
(1000);
} catch (Interruptedexception e) {
e.printstacktrace ();}}
}
;
}.start ();
}
The first execution results, the first execution requires creating a database connection, and so on, so it's slow and time-consuming
After the first time, I modified the database data, so that the starting amount to 9.99, after the scheduled task execution, you can see the printed information as follows
You can see that the starting amount has been modified successfully, the cache is also refreshed correctly, and the reload time is significantly less than the first
After the program is stabilized, what new configuration needs to be added, directly in the Configsfromdb class, new properties, specify the Get method, Configsfromdbimpl basically do not need to modify, so as to reduce the number of new configuration resulting in code writing, After all, laziness is the source of progress.