SPRINGAOP and Redis Build cache

Source: Internet
Author: User
Tags first string serialization

SPRINGAOP and Redis Build cache

Recent project query database is too slow, the persistence layer does not turn on level two cache, and now want to use Redis as a cache. In order not to rewrite the original code, this is implemented using Aop+redis.

Currently, due to the project needs, only the query part is required:

Data query every time you need to query data from the database, the database pressure is very large, query speed is slow, so set up the cache layer, query data from the Redis query, if the query is not, then query the database, and then put the database query data into a redis in a copy, You can find it directly from Redis the next time you query, and you don't need to query the database.

The benefits of Redis as a cache:

1. Memory level cache, query speed is no doubt.

2. High-performance K-V storage System, support string,hash,list,set,sorted Set and other data types, can be applied in many scenarios.

Support for cluster deployment above 3.redis3.0 version.

4.redis supports the persistence of data, Aof,rdb way.

entity classes and tables:

public class Risknote implements Serializable {    private static final long Serialversionuid = 4758331879028183605l;
   private Integer applid;    Private Integer Allqyorg3monnum;    Private Double Loanf6endamt;        Private String IsHighRisk1;    Private Date createdate;    Private String Risk1detail;        Private Integer Risk2;    Private String Risk3;    Private String Creditpaymonth;            ......

Redis and Spring Integration parameters:

Redis.properties

#redis settingsredis.minidle=5redis.maxidle=10redis.maxtotal=50redis.maxwaitmillis=1500redis.testonborrow= Trueredis.numtestsperevictionrun=1024redis.timebetweenevictionrunsmillis=30000redis.minevictableidletimemillis =1800000redis.softminevictableidletimemillis=10000redis.testwhileidle=trueredis.blockwhenexhausted=false# Redisconnectionfactory settingsredis.host=192.168.200.128redis.port=6379

Integration configuration file: Applicationcontext_redis.xml

    <!--load configuration data-<bean class= "Org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > <property name= "Systempropertiesmodename" value= "System_properties_mode_override"/> <property name=                "Ignoreresourcenotfound" value= "true"/> <property name= "Locations" > <list> <value>classpath*:/redis.properties</value> </list> </property> &LT;/BEAN&G    T    <!--annotation Scan-<context:component-scan base-package= "Com.club.common.redis"/> <!--Jedis Connection pool configuration- <bean id= "Poolconfig" class= "Redis.clients.jedis.JedisPoolConfig" > <!--minimum idle connections--<pro Perty name= "Minidle" value= "${redis.minidle}"/> <!--maximum idle connections and <property name= "Maxidle" value = "${redis.maxidle}"/> <!--maximum number of connections--<property name= "Maxtotal" value= "${redis.maxtotal}"/&gt        ; <!--receivedMaximum wait milliseconds to fetch a connection, less than 0: block indeterminate time, Default-1--<property name= "Maxwaitmillis" value= "${redis.maxwaitmillis}"/>        <!--check for validity when getting a connection, by default---<property name= "Testonborrow" value= "${redis.testonborrow}"/> <!--maximum number of connections per release-<property name= "Numtestsperevictionrun" value= "${redis.numtestsperevictionrun}"/ > <!--release connection scan interval (msec)-<property name= "Timebetweenevictionrunsmillis" value= "${redis.timebetwe Enevictionrunsmillis} "/> <!--connection min idle time-<property name=" Minevictableidletimemillis "value=" ${ Redis.minevictableidletimemillis} "/> <!--how long the connection is idle and released when idle time > This value and idle connections > maximum idle Connections is released directly--&LT;PR Operty name= "Softminevictableidletimemillis" value= "${redis.softminevictableidletimemillis}"/> <!--check valid at idle <property name= "Testwhileidle" value= "${redis.testwhileidle}"/> <!--If the connection is exhausted, F Alse Report exception, ture block until timeout, default TruE--<property name= "blockwhenexhausted" value= "${redis.blockwhenexhausted}"/> </bean>        <!--redis Connection pool-<bean id= "Jedispool" class= "Redis.clients.jedis.JedisPool" destroy-method= "Close" > <constructor-arg name= "Poolconfig" ref= "Poolconfig"/> <constructor-arg name= "host" value= "${redis.ho St} "/> <constructor-arg name=" port "value=" ${redis.port} "/> </bean> <bean id=" Rediscach     E "class=" Com.club.common.redis.RedisCache "> <property name=" jedispool "ref=" Jedispool "></property> </bean> <bean id= "Testdao" class= "Com.club.common.redis.TestDao" ></bean> <bean id= "tes TService "class=" Com.club.common.redis.service.TestService ></bean> <!--turn on aspect facets support--&LT;AOP   :aspectj-autoproxy/></beans>

Test, so there are no write interfaces at each level.

The DAO layer queries the data, encapsulating the object:

public class Testdao {//Query public risknote getbyapplid (Integer applid) throws exception{class.f            ORName ("Oracle.jdbc.driver.OracleDriver"); Connection Connection = drivermanager.getconnection ("jdbc:oracle:thin:@192.168.11.215:1521:mftest01", "Datacenter"        , "Datacenter");                PreparedStatement statement = connection.preparestatement ("select * from Temp_risk_note where appl_id=?");        Executive Statement.setint (1, applid);                ResultSet ResultSet = Statement.executequery ();        Risknote risknote = new Risknote ();            Parse while (Resultset.next ()) {Risknote.setapplid (Resultset.getint ("appl_id"));            Risknote.setallqyorg3monnum (Resultset.getint ("Allqyorg3mon_num"));            Risknote.setloanf6endamt (resultset.getdouble ("Loan_f6end_amt"));            Risknote.setishighrisk1 (resultset.getstring ("Is_high_risk_1"));            Risknote.setcreatedate (Resultset.getdate ("create_date")); RIsknote.setrisk1detail (resultset.getstring ("Risk1_detail"));            Risknote.setrisk2 (Resultset.getint ("RISK2"));            RISKNOTE.SETRISK3 (resultset.getstring ("RISK3"));                    Risknote.setcreditpaymonth (resultset.getstring ("Creditpaymonth"));    } return risknote; }}

The service layer calls DAO:

@Servicepublic class Testservice {        @Autowired    private Testdao Testdao;        Public Object get (Integer applid) throws exception{                risknote risknote = Testdao.getbyapplid (applid);                return risknote;}            }

Test:

public class Testqueryrisknote {            @Test public    void Testquery () throws exception{        ApplicationContext ac = new Filesystemxmlapplicationcontext ("Src/main/resources/spring/applicationcontext_redis.xml");        Testservice Testservice = (testservice) ac.getbean ("Testservice");        Risknote risknote = (risknote) testservice.get (91193);        System.out.println (risknote);    }}

At this point the test code output is the query to the Risknote object, you can override the ToString method to view

The results are as follows: Last Output object

In the virtual machine Linux system to build Redis, the specific tutorial please own Baidu

Redis supports a variety of data structures, and objects queried can be stored directly in Redis using a hash structure.

Because of the inconsistent data of the various methods in the project, such as simple objects, a list collection, a map collection, a list of sets of Map objects and other complex structures, in order to achieve uniformity and versatility, Redis also provides a set (byte[],byte[]) method, So the object can be serialized and then deposited into Redis, and then deserialized into the object.

Serialization and deserialization tool classes:

/** * * @Description: Serialization deserialization tool */public class Serializeutil {/** * * serialization */public static byte[] Ser        Ialize (Object obj) {ObjectOutputStream oos = null;                Bytearrayoutputstream BAOs = null;            try {//serialization BAOs = new Bytearrayoutputstream ();                        Oos = new ObjectOutputStream (BAOs);            Oos.writeobject (obj);            byte[] ByteArray = Baos.tobytearray ();                    return byteArray;        } catch (IOException e) {e.printstacktrace ();    } return null;                }/** * * deserialization * @param bytes * @return * * * public static Object unserialize (byte[] bytes) {                Bytearrayinputstream Bais = null;            try {//Deserialize to Object Bais = new Bytearrayinputstream (bytes);            ObjectInputStream ois = new ObjectInputStream (Bais);                    return Ois.readobject (); } catch (Exception e) {e.printstacktrace ();    } return null; }}

Facet Analysis:

Facets: Query Redis before querying, if the query does not penetrate into the database, from the database query to the data, save to Redis, and then the next query can directly hit the cache

The target method is to query the database before querying Redis, which is the predecessor

If it is not found in Redis, then query the database, after executing the target method, you need to put the data of the query to Redis so that the next query will not need to be checked in the database, this is the post

Therefore, you can make the notification in a slice a surround notification

The Slice class is written as follows:

/** * @Description: Tangent: Query Redis before querying, if the query does not penetrate into the database, from the database query to the data, save to Redis, and then the next query can directly hit the cache */@Component @aspectpublic class        Redisaspect {@Autowired @Qualifier ("Rediscache") private Rediscache Rediscache; Set tangency: Use XML to configure @Pointcut in XML ("Execution (* Com.club.common.redis.service.TestService.get (Java.lang.Integer)) and Args (applid)//test, additional method names are specified here, method parameter types, method parameters, and more complete pointcut expressions
   public void Mypointcut () {} @Around ("Mypointcut ()") Public Object Around (Proceedingjoinpoint joinp oint) {//Pre: Query cache System.out.println in Redis ("Invoke Method from Redis ..."); Gets the target method parameter first String applid = null; object[] args = Joinpoint.getargs (); if (args! = null && args.length > 0) {applid = string.valueof (Args[0]); }//redis in key format: applid String rediskey = applid; Gets the object that is queried from Redis object Objectfromredis = Rediscache.getdatafromredis (Rediskey); If you query to the IF (null! = Objectfromredis) {System.out.println ("query data from Redis ... Do not need to query the database "); return Objectfromredis; } System.out.println ("Data not found from Redis ..."); Not found, then query database object object = NULL; try {object = Joinpoint.proceed (); } catch (Throwable e) {e.printstacktrace (); } System.out.println ("Data queried from Database ..."); Post: Put the data queried in the database into Redis System.out.println ("The method that calls the data stored in the database query to Redis ..."); Rediscache.setdatatoredis (Rediskey, object); Returns the queried data to the return object; }}

The method of querying data from Redis and saving database query data to Redis:

/** * * @Description: Redis cache */public class Rediscache {@Resource private jedispool jedispool;    Public Jedispool Getjedispool () {return jedispool;    } public void Setjedispool (Jedispool jedispool) {this.jedispool = Jedispool; }//Query from Redis cache, deserialize public Object Getdatafromredis (String rediskey) {//query Jedis Jedis = Jedispool.get        Resource ();                Byte[] result = Jedis.get (Rediskey.getbytes ());        If the query is not empty if (null = = result) {return null;    }//query to, deserialization return serializeutil.unserialize (result); }//Put the data queried in the database into Redis public void Setdatatoredis (String rediskey, Object obj) {//Serialize Byte                [] bytes = serializeutil.serialize (obj);        Deposit to Redis Jedis Jedis = Jedispool.getresource ();                String success = Jedis.set (rediskey.getbytes (), bytes); if ("OK". Equals (Success)) {SYSTEM.OUT.PRINTLN ("data successfully saved to Redis:.");        }    }} 

Test 1: There is no data for querying objects in Redis at this time

The result is: first to the Redis query, did not find the data, then the agent executes the query from the database, and then the data into a redis, then the next query can be directly from the Redis query

Test 2: At this point, the data from the database has been last queried in Redis

After testing in the project: The effect is still very obvious, there is a super complex query, after the formatted SQL is 688 lines, each refresh page need to re-query, time spent about 10 seconds.

After the first query has been placed on Redis, querying from Redis can result in 2 seconds, very quickly.

Above is the project before the transformation of a demo, the actual project is more complex, the pointcut expression is composed of two or three, but also focus on the expression of the tangent point

Such as:

@Pointcut ("(Execution (* com.club.risk.center.service.impl.*.* (java.lang.String)) | | (Execution (* COM.CLUB.RISK.PY.SERVICE.IMPL.PYSERVCIEIMPL.QUERYPYREPORTBYAPPLID (java.lang.String))) | | (Execution (* com.club.risk.zengxintong.service.Impl.ZXTServiceImpl.queryZxtReportByApplId (..)))

This is a combination of multiple pointcuts to form the use of | | Connection.

The key I used in the actual project was also more complex than applid because it might only use applid to cause key collisions.

So the key used in the project is Applid: The method fully qualified name, so that key can be guaranteed to be inconsistent.

As follows:

Gets the target method parameter first        String applid = null;        object[] args = Joinpoint.getargs ();        if (args! = null && args.length > 0) {           applid = string.valueof (Args[0]);        }                Gets the target method in the same class as        String target = Joinpoint.gettarget (). toString ();        String className = Target.split ("@") [0];                Gets the method name of the target method        String methodName = Joinpoint.getsignature (). GetName ();                Redis in key format:    applid: Method name        String Rediskey = applid + ":" + ClassName + "." + MethodName;

So the above is a generic processing, specific to the project also depends on the specific situation.

Previously did not write the AOP code, this use suddenly found that AOP is really powerful, throughout the process in addition to the configuration file I did not change any of the previous source code, the function is all cut in.

This demo also basically implements the requirements, only need to set the pointcut, be able to apply the cache to various query methods, or set the tangent to Service.impl package, directly acting on all service methods.

Category: Caching technology

SPRINGAOP and Redis Build cache

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.