Hintergrund
SpringBoot hat es heute zu einem der monatlichsten Java-Webentwicklungs-Frameworks gemacht, da es eine Vielzahl von außergewöhnlichen Plugins bietet. MyBatis ist ein sehr leichtes und benutzerfreundliches Orm-Framework. Redis ist heute eine sehr Mainstream-Verteilungsdatenbank. In der Webentwicklung verwenden wir häufig die Ergebnisse der Datenbankabfrage.
In diesem Blog werden mit Springboot schnell eine Webanwendung erstellt und MyBatis als ORM -Framework verwendet. Um die Leistung zu verbessern, verwenden wir Redis als Cache der zweiten Ebene für MyBatis. Um unseren Code zu testen, haben wir Unit-Tests geschrieben und die H2-In-Memory-Datenbank verwendet, um unsere Testdaten zu generieren. Durch dieses Projekt hoffen wir, dass die Leser schnell die Fähigkeiten und Best Practices der modernen Java -Webentwicklung beherrschen können.
Der Beispielcode für diesen Artikel kann in GitHub heruntergeladen werden: https://github.com/lovelcp/spring-boot-mybatis-with-redis/tree/master
Umfeld
Entwicklungsumgebung: MAC 10.11
IDE: Intellij 2017.1
JDK: 1.8
Springboot: 1.5.3.Release
Redis: 3.2.9
MySQL: 5.7
Frühlingsboot
Erstellen Sie ein neues Projekt
Zunächst müssen wir unser Spring-Boot-Projekt initialisieren. Durch den Spring Initializer von Intellij ist es sehr einfach, ein neues Spring-Boot-Projekt zu erstellen. Zunächst wählen wir ein neues Projekt in Intellij aus:
Überprüfen Sie dann in der Schnittstelle, um Abhängigkeiten auszuwählen, Web, MyBatis, Redis, MySQL, H2:
Nachdem das neue Projekt erfolgreich ist, können wir die anfängliche Struktur des Projekts sehen, wie in der folgenden Abbildung gezeigt:
Spring Initializer hat uns geholfen, automatisch eine Startklasse zu generieren - SpringbootmybatiswithRedISApplication. Der Code dieser Klasse ist sehr einfach:
@SpringbootApplicationPublic Class SpringbootMybatisWithReTApplication {public static void main (String [] args) {SpringApplication.run (SpringbootMybatiswithReTApplication.Class, Args); }}@SpringbootApplication Annotation bedeutet, die automatische Konfigurationsfunktion des Spring -Boots zu aktivieren. Okay, unser Projektskelett wurde erfolgreich gebaut, damit interessierte Leser die Ergebnisse durch Intellij beginnen können.
Erstellen Sie eine neue API -Schnittstelle
Als nächstes schreiben wir eine Web -API. Angenommen, unsere Web -Engineering ist für die Behandlung der Produkte des Händlers (Produkt) verantwortlich. Wir müssen eine Get -Schnittstelle bereitstellen, die Produktinformationen basierend auf der Produkt -ID und einer Put -Schnittstelle zurückgibt, die Produktinformationen aktualisiert. Zuerst definieren wir die Produktklasse, die die Produkt -ID, den Produktnamen und den Preis enthält:
Produkte im öffentlichen Klassen implementiert serialisierbar {private statische endgültige lange Serialversionuid = 1435515995276255188L; private lange Ausweis; privater Zeichenfolge Name; privat langer Preis; // Getters Setter}Dann müssen wir die Controller -Klasse definieren. Da Spring Boot Spring MVC als interne Webkomponente verwendet, können wir unsere Schnittstellenklasse durch Annotation schnell entwickeln:
@RastController @RequestMapping ("/produkt") public class ProductController {@getmapping ("/{id}") öffentliches Produkt getProductInfo (@PathVariable ("id") Long ProductID) {// Todo return null; } @Putmapping ("/{id}") public Product UpdateProductinFo (@PathVariable ("id") Long ProductID, @RequestBody Product NewProduct) {// Todo return null; }}Lassen Sie uns kurz die Funktionen der im obigen Code verwendeten Anmerkungen vorstellen:
@RestController: bedeutet, dass die Klasse ein Controller ist und eine REST -Schnittstelle bietet, dh die Werte aller Schnittstellen werden im JSON -Format zurückgegeben. Diese Annotation ist eigentlich eine Kombination von @Controller und @ResponseBody, die uns erleichtert, die Rest -API zu entwickeln.
@RequestMapping, @getMapping, @putmaping: repräsentiert die URL -Adresse der Schnittstelle. Die in der Klasse kommentierte Annotation @RequestMapping -Annotation bedeutet, dass die URLs aller Schnittstellen unter der Klasse mit /Produkt beginnen. @GetMapping bedeutet, dass dies eine GET -HTTP -Schnittstelle ist. @Putmapping bedeutet, dass dies eine Put -HTTP -Schnittstelle ist.
@PathVariable, @RequestBody: Repräsentiert die Zuordnungsbeziehung von Parametern. Unter der Annahme, dass ein GET -Anforderungszugriff /Produkt /123 die Anforderung von der GetProductinFO -Methode bearbeitet wird, wobei 123 in der URL in die Produktid zugeordnet werden. Wenn es sich um eine Put -Anfrage handelt, wird das angeforderte Körper in das NewProduct -Objekt zugeordnet.
Hier definieren wir nur die Schnittstelle, und die tatsächliche Verarbeitungslogik wurde noch nicht abgeschlossen, da die Informationen des Produkts in der Datenbank vorhanden sind. Als nächstes werden wir MyBatis in das Projekt integrieren und mit der Datenbank interagieren.
Integration von MyBatis
Datenquelle konfigurieren
Zuerst müssen wir unsere Datenquelle in der Konfigurationsdatei konfigurieren. Wir verwenden MySQL als unsere Datenbank. Hier verwenden wir YAML als Format unserer Konfigurationsdatei. Wir erstellen im Ressourcenverzeichnis eine neue Datei für application.yml:
Spring:# Datenbankkonfiguration DataSource: URL: JDBC: MySQL: // {your_host}/{your_db} username: {your_username} Passwort: {your_password} Treiberklassen-Name: org.gjt.mm.mysql.driverDa Spring Boot über die automatische Konfigurationsfunktion verfügt, müssen wir keine neue DataSource -Konfigurationsklasse erstellen. Spring Start lädt die Konfigurationsdatei automatisch und erstellt einen Datenbankverbindungspool basierend auf den Konfigurationsdateisinformationen, die sehr bequem sind.
Der Autor empfiehlt, YAML als Konfigurationsdateiformat zu verwenden. XML sieht langwierig aus und Eigenschaften haben keine hierarchische Struktur. Yaml macht nur die Mängel beider. Dies ist auch der Grund, warum Spring Boot standardmäßig das YAML -Format unterstützt.
Konfigurieren Sie MyBatis
Wir haben die MyBatis-String-Boot-Starte-Bibliothek in pom.xml durch Spring Initializer vorgestellt, die uns automatisch hilft, MyBatis zu initialisieren. Zunächst füllen wir die relevante Konfiguration von myBatis in application.yml aus:
# MyBatis Konfigurieren Sie MyBatis: # Konfigurieren Sie den Paketnamen, in dem sich die Zuordnungsklasse befindet.
Definieren Sie dann die ProductMapper -Klasse im Code:
@Mapperpublic interface productMapper {product select (@param ("id") long id); void Update (Produktprodukt);}Solange wir die @Mapper -Annotation hinzufügen, lädt Spring Boot die Mapper -Klasse beim Initialisieren von MyBatis automatisch.
Der Hauptgrund, warum Spring Boot so beliebt ist, ist die automatische Konfigurationsfunktion. Entwickler müssen nur auf die Konfiguration von Komponenten (z. B. Datenbankverbindungsinformationen) achten, ohne sich darum zu kümmern, wie einzelne Komponenten initialisiert werden, sodass wir uns auf die Unternehmensimplementierung konzentrieren und den Entwicklungsprozess vereinfachen können.
Zugriff auf die Datenbank
Nach Abschluss der MyBatis -Konfiguration können wir auf die Datenbank in unserer Schnittstelle zugreifen. Wir stellen die Mapper -Klasse über @autowired unter ProductController ein und rufen die entsprechende Methode auf, um Abfragen zu implementieren und Vorgänge auf Produkt zu aktualisieren. Hier nehmen wir die Query -Schnittstelle als Beispiel:
@RastController @requestMapping ("/product") public class ProductController {@autowired Private ProductMapper ProductMapper; @GetMapping ("/{id}") öffentliches Produkt getProductInfo (@PathVariable ("id") Long ProductID) {return productMapper.Select (productId); } // Vermeiden Sie zu lang und lassen Sie den Code von UpdateProductinfo} weg}Fügen Sie dann ein paar Produktinformationen in Ihr MySQL ein und Sie können das Projekt ausführen, um festzustellen, ob die Abfrage erfolgreich ist.
Bisher haben wir MyBatis erfolgreich in unser Projekt integriert und die Möglichkeit hinzugefügt, mit der Datenbank zu interagieren. Aber das ist nicht genug. Ein modernes Webprojekt wird unsere Datenbankabfrage auf Cache definitiv beschleunigen. Als nächstes werden wir vorstellen, wie Sie Redis wissenschaftlich in MyBatis 'sekundäres Cache integrieren können, um den automatischen Cache von Datenbankabfragen zu realisieren.
Integrierte Redis
Konfigurieren Sie Redis
Genau wie beim Zugriff auf eine Datenbank müssen wir Redis -Verbindungsinformationen konfigurieren. Fügen Sie der Datei application.yml die folgende Konfiguration hinzu:
Spring: Redis: # Redis -Datenbankindex (Standard ist 0). Wir verwenden eine Datenbank mit Index 3, um Konflikte mit anderen Datenbankdatenbank zu vermeiden: 3 # REDIS -Serveradresse (Standard ist localhost) Host: Localhost # Redis -Port (Standard ist 6379) Port: 6379 # Redis Access (Standard ist Null). Zahlen repräsentieren unendlich) max-aktiv: 8 # Maximale Anzahl von Leerlaufverbindungen (Standard ist 8, negative Zahlen repräsentieren unendlich) max-idle: 8 # minimale Anzahl von Leerlaufverbindungen (Standard ist 0, dieser Wert ist nur wirksam) min-idle: 0 # Die maximale Verbindungszeit abrufen Wartezeit aus dem Verbindungspool (Defaugt is1, Einheit Is.
Alle oben genannten aufgeführt werden häufig verwendete Konfigurationen, und die Leser können die spezifische Rolle jedes Konfigurationselements durch Kommentarinformationen verstehen. Seit wir die Spring-Boot-Starter-Data-Redis-Bibliothek in pom.xml eingeführt haben, hilft uns Spring Start automatisch Redis-Verbindungen und spezifische Konfigurationsklassen
org.springframework.boot.autoconfigure.data.redis.redisautoconfiguration. Über diese Konfigurationsklasse können wir feststellen, dass die zugrunde liegende Ebene die Jedis -Bibliothek standardmäßig verwendet und redistemplatte und StringTemplate außerhalb des Feldes bietet.
Verwenden Sie Redis als Cache Level 2
Das Prinzip von MyBatis 'sekundärem Unterhalt wird in diesem Artikel nicht beschrieben. Die Leser müssen nur wissen, dass MyBatis 'sekundäres Caching Datenbankabfragen automatisch zwischenspeichern und den Cache beim Aktualisieren von Daten automatisch aktualisieren kann.
Die Implementierung von MyBatis 'sekundärem Caching ist sehr einfach. Sie müssen nur eine neue Klasse erstellen, um die Schnittstelle org.apache.ibatis.cache.cache zu implementieren.
Es gibt fünf Methoden für diese Schnittstelle:
String getId (): Die Kennung des MyBatis -Cache -Betriebsobjekts. Ein Mapper entspricht einem MyBatis -Cache -Operationsobjekt.
void putObject (Objektschlüssel, Objektwert): Stimmen Sie die Abfrage in den Cache.
Objekt getObject (Objektschlüssel): Holen Sie sich das zwischengespeicherte Abfrageergebnis aus dem Cache.
Object RemectObject (Objekttaste): Entfernen Sie den entsprechenden Schlüssel und den Wert aus dem Cache. Nur beim Zurückrollen gefeuert. Im Allgemeinen müssen wir es nicht implementieren. Für bestimmte Verwendungsmethoden finden Sie unter: org.apache.ibatis.cache.decorators.transactionalcache.
void clear (): Cache löschen, wenn ein Update auftritt.
Int getSize (): Optionale Implementierung. Gibt die Anzahl der Caches zurück.
ReadWriteLock GetReadWriteLock (): Optionale Implementierung. Wird verwendet, um Atom -Cache -Operationen zu implementieren.
Als nächstes erstellen wir eine neue Rediscache -Klasse, um die Cache -Schnittstelle zu implementieren:
public class rediscache implementiert cache {private static final logger logger = loggerfactory.getLogger (rediscache.class); Private Final ReadWriteLock ReadWriteLock = New ReentranTreadWriteLock (); private endgültige String -ID; // Cache -Instanz -ID private redistemplate redistemplate; private statische endgültige long expire_time_in_minutes = 30; // Redis -Ablaufzeit public rediscache (String -ID) {if (id == null) {werfen Sie neue illegalArgumentException ("Cache -Instanzen erfordern eine ID"); } this.id = id; } @Override public String getId () {return id; } / ** * Abfragenergebnis in redis * * @param key * @param value * / @Override @SuppressWarnings ("deaktiviert") public void putObject (Objektschlüssel, Objektwert) {redistemplate redistemplate = getRedistemplate (); ValueOperations opsforValue = redistemplate.opsforValue (); opsforValue.set (Schlüssel, Wert, expire_time_in_minutes, timeunit.minutes); logger.debug ("Abfrageergebnis zu Redis"); } / ** * ERGEBNISSE ERGEBNISSE ERGEBNISSE VON REDIS * * @PARAM KEY * @RETURN * / @Override public Object getObject (Objektschlüssel) {redistemplate redISTemplate = getRedistemplate (); ValueOperations opsforValue = redistemplate.opsforValue (); logger.debug ("Get zwischengespeicherte Abfrageergebnis von Redis"); return opsforValue.get (Schlüssel); } / ** * Entfernen Sie das zwischengespeicherte Abfrageergebnis aus Redis * * @param key * @return * / @Override @SuppressWarnings ("deaktiviert") öffentliches Objekt REMETOBJECT (Objektschlüssel) {redistemplate redISTemplate = getRedistemplate (); redistemplate.delete (Schlüssel); logger.debug ("Entfernen Sie zwischengeweihter Abfrage aus Redis"); null zurückkehren; } / ** * löscht diese Cache -Instanz * / @Override public void clear () {redistemplate redistemplate = getRedistemplate (); redistemplate.execute ((reciscallback) Verbindung -> {connection.flushdb (); return null;}); logger.debug ("Alle zwischengespeicherten Abfragen aus Redis löschen"); } @Override public int getSize () {return 0; } @Override public ReadWriteLock getReadWriteLock () {return readWriteLock; } private redistemplate getRedistemplate () {if (redistemplate == null) {redistemplate = applicationContexTHolder.getbean ("redistemplate"); } return redistemplate; }}Lassen Sie mich einige wichtige Punkte im obigen Code erklären:
Der von Ihnen implementierte Cache auf der zweiten Ebene muss einen Konstruktor mit ID haben, andernfalls wird ein Fehler gemeldet.
Wir verwenden die federkapselte Redistemplate, um Redis zu bedienen. Alle Artikel online, die Redis in den sekundären Cache von Level 2 einführen, verwenden die Jedis -Bibliothek direkt. Der Autor ist jedoch der Ansicht, dass dies nicht genug Frühlingsstil ist. Darüber hinaus verkauft Redistemplate die zugrunde liegende Implementierung. Wenn wir in Zukunft Jedis nicht verwenden, können wir die zugrunde liegende Bibliothek direkt ersetzen, ohne den oberen Code zu ändern. Was bequemer ist, ist, dass wir die Verwendung von Redistemplate nicht um die Veröffentlichung von Redis -Verbindungen kümmern müssen. Andernfalls ist es für Anfänger einfach, zu vergessen, die Verbindung zu veröffentlichen und die Anwendung festzuhalten.
Es ist zu beachten, dass Redistemplate nicht über AutoWire referenziert werden kann, da Rediscache keine Bohne im Federbehälter ist. Deshalb müssen wir die GetBean -Methode des Containers manuell anrufen, um diese Bohne zu erhalten. Für bestimmte Implementierungsmethoden finden Sie im Code in GitHub.
Die von uns verwendete Redis -Serialisierungsmethode ist die Standard -JDK -Serialisierung. Daher muss das Datenbank -Abfrageobjekt (wie die Produktklasse) die serialisierbare Schnittstelle implementieren.
Auf diese Weise implementieren wir eine elegante, wissenschaftliche und Redis -Cache -Klasse mit Frühlingsstil.
Schalten Sie Level 2 Cache ein
Als nächstes müssen wir Level 2 Cache in productMapper.xml aktivieren:
<? namespace = "com.wooyoo.learning.dao.mapper.ProductMapper"> <!-REDIS-basierte sekundäre Cache aktivieren-> <cache type = "com.wooyoo.learning parameterType = "product" "flushcache =" true "> Produkte set name = #{name}, price = #{price} wobei ID = #{id} Limit 1 </upds> </mapper><cache type = "com.wooyoo.learning
prüfen
Konfigurieren Sie die H2 -Speicherdatenbank
Zu diesem Zeitpunkt haben wir die gesamte Codeentwicklung abgeschlossen und als nächstes müssen wir den Testcode des Unit -Tests schreiben, um die Qualität unseres Codes zu testen. Im Entwicklungsprozess haben wir die MySQL-Datenbank verwendet und im Allgemeinen häufig In-Memory-Datenbanken während des Tests verwendet. Hier verwenden wir H2 als die in unserem Testszenario verwendete Datenbank.
Es ist auch sehr einfach, H2 zu verwenden, Sie müssen es nur konfigurieren, wenn Sie MySQL verwenden. In der Datei application.yml:
--- Spring: Profile: Testen Sie # Datenbankkonfiguration DataSource: URL: JDBC: H2: MEM: Test Benutzername: Root Passwort: 123456 Treiberklasse-Name: org.h2.Driver Schema: ClassPath: Schema.SQL Data: ClassPath: Data.SQL
Um Konflikte mit der Standardkonfiguration zu vermeiden, verwenden wir-zum Starten eines neuen Absatzes und zur Verwendung von Profilen: Test, um anzuzeigen, dass dies die Konfiguration in der Testumgebung ist. Fügen Sie dann einfach die Annotation @ActiveProfiles (Profiles = "Test") zu unserer Testklasse hinzu, um die Konfiguration in der Testumgebung zu aktivieren, damit Sie mit einem Klick aus der MySQL -Datenbank in die H2 -Datenbank wechseln können.
In der obigen Konfiguration wird Schema.sql verwendet, um unsere Anweisung für die Erstellung von Tabellen zu speichern, und data.sql wird zum Speichern von Daten verwendet. Auf diese Weise wird beim Testen H2 diese beiden Dateien lesen, die von uns benötigte Tabellenstruktur und Daten initialisieren und dann am Ende des Tests zerstören, was keinen Einfluss auf unsere MySQL -Datenbank hat. Dies ist der Vorteil von In-Memory-Datenbanken. Vergessen Sie auch nicht, den Umfang der Abhängigkeit von H2 in pom.xml zu testen.
Wenn Sie Spring Boot so einfach verwenden, können Sie die Datenbanken einfach in verschiedenen Umgebungen wechseln, ohne Code zu ändern.
Schreiben von Testcode
Da wir über den Spring Initializer initialisiert werden, haben wir bereits eine Testklasse - SpringbootMybatiswithRedisapplicationTests.
Spring Boot bietet einige Werkzeugkurse, die uns erleichtern, Weboberflächen -Tests wie TestRestTemplate durchzuführen. In der Konfigurationsdatei passen wir dann die Protokollebene an Debuggen an, um die Beobachtung von Debug -Protokollen zu erleichtern. Der spezifische Testcode lautet wie folgt:
@Runwith (springrunner.class) @springboottest (webenvironment = SpringBoottest.Webenvironment.random_port) @activeProfile (Profiles = "test") öffentlicher Klasse SpringbootMyBatisWitHedIsApplicationTests {@localServerport privat int int; @Autowired Private TestRestTemplate RestTemplate; @Test public void test () {Long ProductID = 1; Product product = rastTemplate.getForObject ("http: // localhost:" + port + "/product/" + productId, product.class); AssertThat (product.getPrice ()). isequalto (200); Produkt newProduct = new Product (); long NewPrice = new random (). Nextlong (); NewProduct.SetName ("neuer Name"); NewProduct.setPrice (NewPrice); restTemplate.put ("http: // localhost:" + port + "/product/" + productId, newProduct); Produkt testproduct = restTemplate.getforObject ("http: // localhost:" + port + "/product/" + productId, product.class); AssertThat (testProduct.getPrice ()). isequalto (newPrice); }}Im obigen Testcode:
Wir rufen zunächst die GET -Schnittstelle auf und verwenden die Assert -Anweisung, um festzustellen, ob das erwartete Objekt erhalten wurde. Zu diesem Zeitpunkt wird das Produktobjekt in Redis gespeichert.
Anschließend rufen wir die Put -Schnittstelle an, um das Produktobjekt zu aktualisieren, und der Redis -Cache wird ungültig gemacht.
Schließlich rufen wir die Get -Schnittstelle erneut auf, um festzustellen, ob wir ein neues Produktobjekt erhalten haben. Wenn ein altes Objekt erhalten wird, bedeutet dies, dass der Cache ungültige Code nicht ausgeführt wurde und im Code ein Fehler vorliegt. Andernfalls bedeutet dies, dass unser Code in Ordnung ist.
Das Schreiben von Unit -Tests ist eine gute Programmiergewohnheit. Obwohl es für Sie eine gewisse Zeit in Anspruch nimmt, werden Sie sich selbst dankbar sein, die in der Vergangenheit eine Refactoring -Arbeit in Zukunft erledigen müssen.
Sehen Sie sich die Testergebnisse an
Wir klicken, um den Testfall in Intellij auszuführen, und die Testergebnisse sind wie folgt:
Das Grün wird angezeigt, was darauf hinweist, dass der Testfall erfolgreich ausgeführt wurde.
Zusammenfassen
In diesem Artikel werden mit Spring Boot, MyBatis und Redis schnell ein modernes Webprojekt erstellt und auch vorgestellt, wie Unit -Tests unter Spring Boot anmutig geschrieben werden, um die Qualität unseres Codes zu gewährleisten. Natürlich gibt es ein weiteres Problem mit diesem Projekt, dh MyBatis 'Level -2 -Cache kann nur durch Spülen der gesamten DB zwischengespürt werden. Zu diesem Zeitpunkt können einige Caches, die nicht ungültig werden müssen, auch ungültig werden, sodass es bestimmte Einschränkungen aufweist.