Dina iki kita bakal nindakake proyek pungkasan ing modul JRU kaping papat. Apa bakal dadi? Ayo nyoba nggarap macem-macem teknologi: MySQL, Hibernate, Redis, Docker. Saiki luwih subyek.

Tugas: kita duwe database MySQL relasional kanthi skema (negara-kutha, basa miturut negara). Lan ana panjalukan kutha sing kerep, sing dadi alon. We teka munggah karo solusi - kanggo mindhah kabeh data sing kerep dijaluk kanggo Redis (ing panyimpenan memori saka jinis tombol-nilai).

Lan kita ora butuh kabeh data sing disimpen ing MySQL, nanging mung sawetara lapangan sing dipilih. Proyek kasebut bakal dadi tutorial. Yaiku, ing kene kita bakal ngunggahake masalah kasebut lan langsung ngrampungake.

Dadi, ayo miwiti piranti lunak apa sing dibutuhake:

  1. IDEA Ultimate (sing kehabisan kunci - nulis menyang Roman ing Slack)
  2. Workbench (utawa klien liyane kanggo MySQL)
  3. Docker
  4. redis-wawasan - opsional

Rencana aksi kita:

  1. Nggawe docker (Aku ora bakal nindakake iki ing tutorial, amarga saben OS bakal duwe ciri dhewe lan ana akeh jawaban ing Internet kanggo pitakonan kaya "carane nginstal docker ing windows"), priksa manawa kabeh bisa digunakake.
  2. Jalanake server MySQL minangka wadhah docker.
  3. Ngembangake dump .
  4. Nggawe proyek ing Idea, nambah dependensi maven.
  5. Nggawe domain lapisan.
  6. Tulis cara kanggo njupuk kabeh data saka MySQL.
  7. Nulis metode transformasi data (ing Redis kita bakal nulis mung data sing asring dijaluk).
  8. Jalanake server Redis minangka wadhah docker.
  9. Tulis data menyang Redis.
  10. Opsional: instal redis-inight, deleng data sing disimpen ing Redis.
  11. Tulis cara kanggo njupuk data saka Redis.
  12. Tulis cara kanggo njupuk data saka MySQL.
  13. Mbandhingake kacepetan njupuk data sing padha saka MySQL lan Redis.

Persiyapan Docker

Docker minangka platform mbukak kanggo ngembangake, ngirim lan ngoperasikake aplikasi. Kita bakal nggunakake supaya ora nginstal lan ngatur Redis ing mesin lokal, nanging nggunakake gambar siap-digawe. Sampeyan bisa maca liyane babagan docker kene utawa ndeleng kene . Yen sampeyan ora kenal karo docker, aku nyaranake ndeleng mung link kapindho.

Kanggo mesthekake yen sampeyan wis nginstal lan ngatur docker, jalanake perintah kasebut:docker -v

Yen kabeh OK, sampeyan bakal weruh versi docker

Jalanake server MySQL minangka wadhah docker

Supaya bisa mbandhingake wektu bali data saka MySQL lan Redis, kita uga bakal nggunakake MySQL ing docker. Ing PowerShell (utawa terminal console liyane yen sampeyan ora nggunakake Windows), jalanake printah:

docker run --name mysql -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root --restart unless-stopped -v mysql:/var/lib/mysql mysql:8 

Coba apa sing kita tindakake karo printah iki:

  • docker run- miwiti (lan ngundhuh, yen durung diundhuh menyang mesin lokal) gambar kasebut. Minangka asil peluncuran, kita entuk wadhah sing mlaku.
  • --name mysql- nyetel jeneng wadhah mysql.
  • -d- flag sing ngandika sing wadhah kudu terus bisa, malah yen sampeyan nutup jendhela terminal saka kang wadhah iki dibukak.
  • -p 3306:3306- nemtokake port. Sadurunge titik loro - port ing mesin lokal, sawise titik titik - port ing wadhah.
  • -e MYSQL_ROOT_PASSWORD=root– ngliwati variabel lingkungan MYSQL_ROOT_PASSWORD kanthi nilai ROOT menyang wadhah. Flag khusus kanggo mysql / gambar
  • --restart unless-stopped- nyetel kabijakan prilaku (apa wadhah kudu diwiwiti maneh nalika ditutup). Nilai kajaba-mandheg tegese tansah miwiti maneh, kajaba nalika wadhah mandheg /
  • -v mysql:/var/lib/mysql - nambah volume (gambar kanggo nyimpen informasi).
  • mysql:8 – jeneng gambar lan versi.

Sawise nglakokake printah ing terminal, docker bakal ndownload kabeh lapisan gambar lan miwiti wadhah:

Cathetan penting: yen sampeyan wis nginstal MySQL minangka layanan ing komputer lokal lan lagi mlaku, sampeyan kudu nemtokake port sing beda ing printah wiwitan, utawa mungkasi layanan sing mlaku iki.

Expand dump

Kanggo nggedhekake mbucal, sampeyan kudu nggawe sambungan anyar kanggo database saka Workbench, ngendi sampeyan nemtokake paramèter. Aku nggunakake port standar (3306), ora ngganti jeneng panganggo (root minangka standar), lan nyetel sandhi kanggo pangguna root (root).

Ing Workbench, tindakake Data Import/Restorebanjur pilih Import from Self Contained File. Nemtokake ngendi sampeyan ngundhuh dump minangka file . Sampeyan ora perlu nggawe skema sadurunge - nggawe wis kalebu ing file dump. Sawise ngimpor sukses, sampeyan bakal duwe skema donya kanthi telung tabel:

  1. kutha iku tabel kutha.
  2. meja negara - negara.
  3. country_language - tabel sing nuduhake apa persentasi saka populasi ing negara ngandika basa tartamtu.

Awit kita nggunakake volume nalika miwiti wadhah, sawise mandheg lan malah mbusak wadhah mysql lan nglakokake maneh perintah wiwitan ( docker run --name mysql -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root --restart unless-stopped -v mysql:/var/lib/mysql mysql:8), ora perlu nyebarke dump maneh - wis disebarake ing volume.

Nggawe proyek ing Idea, nambah dependensi maven

Sampeyan wis ngerti carane nggawe proyek ing Idea - iki minangka titik paling gampang ing proyek saiki.

Tambah dependensi menyang file pom:


<dependencies> 
   <dependency> 
      <groupId>mysql</groupId> 
      <artifactId>mysql-connector-java</artifactId> 
      <version>8.0.30</version> 
   </dependency> 
 
   <dependency> 
      <groupId>org.hibernate</groupId> 
      <artifactId>hibernate-core-jakarta</artifactId> 
      <version>5.6.14.Final</version> 
   </dependency> 
 
   <dependency> 
      <groupId>p6spy</groupId> 
      <artifactId>p6spy</artifactId> 
      <version>3.9.1</version> 
   </dependency> 
    
   <dependency> 
      <groupId>io.lettuce</groupId> 
      <artifactId>lettuce-core</artifactId> 
      <version>6.2.2.RELEASE</version> 
   </dependency> 
 
   <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId> 
      <artifactId>jackson-databind</artifactId> 
      <version>2.14.0</version> 
   </dependency> 
</dependencies> 

Telung dependensi pisanan wis suwe sampeyan kenal.

lettuce-coreminangka salah sawijining klien Java sing kasedhiya kanggo nggarap Redis.

jackson-databind- ketergantungan kanggo nggunakake ObjectMapper (kanggo ngowahi data kanggo panyimpenan ing Redis (nilai kunci saka jinis String)).

Uga ing folder sumber daya (src / utama / sumber) nambah spy.properties kanggo ndeleng panjalukan karo paramèter sing Hibernate kaleksanan. Isi berkas:

driverlist=com.mysql.cj.jdbc.Driver 
dateformat=yyyy-MM-dd hh:mm:ss a 
appender=com.p6spy.engine.spy.appender.StdoutLogger 
logMessageFormat=com.p6spy.engine.spy.appender.MultiLineFormat 

Nggawe domain lapisan

Nggawe paket com.codegym.domain

Iku trep kanggo kula nalika pemetaan tabel ing entitas nggunakake struktur Tabel ing Idea, supaya nambah sambungan database ing Idea.

Aku saranake nggawe entitas ing urutan iki:

  • negara
  • kutha
  • Basa Negara

Disaranake sampeyan nindakake pemetaan dhewe.

Kode kelas negara:

package com.codegym.domain;

import jakarta.persistence.*;

import java.math.BigDecimal;
import java.util.Set;

@Entity
@Table(schema = "world", name = "country")
public class Country {
    @Id
    @Column(name = "id")
    private Integer id;

    private String code;

    @Column(name = "code_2")
    private String alternativeCode;

    private String name;

    @Column(name = "continent")
    @Enumerated(EnumType.ORDINAL)
    private Continent continent;

    private String region;

    @Column(name = "surface_area")
    private BigDecimal surfaceArea;

    @Column(name = "indep_year")
    private Short independenceYear;

    private Integer population;

    @Column(name = "life_expectancy")
    private BigDecimal lifeExpectancy;

    @Column(name = "gnp")
    private BigDecimal GNP;

    @Column(name = "gnpo_id")
    private BigDecimal GNPOId;

    @Column(name = "local_name")
    private String localName;

    @Column(name = "government_form")
    private String governmentForm;

    @Column(name = "head_of_state")
    private String headOfState;

    @OneToOne
    @JoinColumn(name = "capital")
    private City city;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "country_id")
    private Set<CountryLanguage> languages;


    //Getters and Setters omitted

}

Ana 3 titik menarik ing kode.

Kapisan yaiku enam Continent , sing disimpen ing basis data minangka nilai ordinal. Ing struktur tabel negara, ing komentar menyang lapangan bawana, sampeyan bisa ndeleng nilai numerik sing cocog karo bawana.

package com.codegym.domain;

public enum Continent {
    ASIA,
    EUROPE,
    NORTH_AMERICA,
    AFRICA,
    OCEANIA,
    ANTARCTICA,
    SOUTH_AMERICA
}

Titik kapindho yaiku sakumpulan entitasCountryLanguage . Iki link @OneToManysing ora ana ing draf kapindho modul iki. Kanthi gawan, Hibernate ora bakal narik nilai set iki nalika njaluk entitas negara. Nanging amarga kita kudu nyuda kabeh nilai saka database relasional kanggo caching, FetchType.EAGER.

Sing nomer telu yaiku lapangan kutha . Komunikasi @OneToOne- kaya kabeh sing akrab lan bisa dingerteni. Nanging, yen kita ndeleng struktur kunci asing ing basis data, kita bakal weruh yen negara (negara) duwe pranala menyang ibukutha (kutha), lan kutha (kutha) duwe pranala menyang negara (negara). Ana hubungan cyclical.

Kita ora bakal nindakake apa-apa, nanging nalika kita tekan item "Tulis metode kanggo njupuk kabeh data saka MySQL", ayo ndeleng pitakon apa sing ditindakake Hibernate, deleng nomer kasebut, lan elinga item iki.

Kode Kelas Kutha:

package com.codegym.domain;

import jakarta.persistence.*;

@Entity
@Table(schema = "world", name = "city")
public class City {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "country_id")
    private Country country;

    private String district;

    private Integer population;


    //Getters and Setters omitted

}

Kode kelas Basa Negara:

package com.codegym.domain;

import jakarta.persistence.*;
import org.hibernate.annotations.Type;

import java.math.BigDecimal;

@Entity
@Table(schema = "world", name = "country_language")
public class CountryLanguage {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    @ManyToOne
    @JoinColumn(name = "country_id")
    private Country country;

    private String language;

    @Column(name = "is_official", columnDefinition = "BIT")
    @Type(type = "org.hibernate.type.NumericBooleanType")
    private Boolean isOfficial;

    private BigDecimal percentage;


    //Getters and Setters omitted
}

Tulis cara kanggo njupuk kabeh data saka MySQL

Ing kelas Utama, kita nyatakake lapangan:

private final SessionFactory sessionFactory;
private final RedisClient redisClient;

private final ObjectMapper mapper;

private final CityDAO cityDAO;
private final CountryDAO countryDAO;

lan miwiti ing konstruktor kelas Utama:

public Main() {
    sessionFactory = prepareRelationalDb();
    cityDAO = new CityDAO(sessionFactory);
    countryDAO = new CountryDAO(sessionFactory);

    redisClient = prepareRedisClient();
    mapper = new ObjectMapper();
}

Kaya sing sampeyan ngerteni, ora ana cara lan kelas sing cukup - ayo nulis.

Nyatakake paket com.codegym.dao lan tambahake 2 kelas:

package com.codegym.dao;

import com.codegym.domain.Country;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;

import java.util.List;

public class CountryDAO {
    private final SessionFactory sessionFactory;

    public CountryDAO(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public List<Country> getAll() {
        Query<Country> query = sessionFactory.getCurrentSession().createQuery("select c from Country c", Country.class);
        return query.list();
    }
}
package com.codegym.dao;

import com.codegym.domain.City;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;

import java.util.List;

public class CityDAO {
    private final SessionFactory sessionFactory;

    public CityDAO(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public List<City> getItems(int offset, int limit) {
        Query<City> query = sessionFactory.getCurrentSession().createQuery("select c from City c", City.class);
        query.setFirstResult(offset);
        query.setMaxResults(limit);
        return query.list();
    }

    public int getTotalCount() {
        Query<Long> query = sessionFactory.getCurrentSession().createQuery("select count(c) from City c", Long.class);
        return Math.toIntExact(query.uniqueResult());
    }
}

Saiki sampeyan bisa ngimpor 2 kelas kasebut menyang Utama. Isih ilang rong cara:

private SessionFactory prepareRelationalDb() {
    final SessionFactory sessionFactory;
    Properties properties = new Properties();
    properties.put(Environment.DIALECT, "org.hibernate.dialect.MySQL8Dialect");
    properties.put(Environment.DRIVER, "com.p6spy.engine.spy.P6SpyDriver");
    properties.put(Environment.URL, "jdbc:p6spy:mysql://localhost:3306/world");
    properties.put(Environment.USER, "root");
    properties.put(Environment.PASS, "root");
    properties.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
    properties.put(Environment.HBM2DDL_AUTO, "validate");
    properties.put(Environment.STATEMENT_BATCH_SIZE, "100");

    sessionFactory = new Configuration()
            .addAnnotatedClass(City.class)
            .addAnnotatedClass(Country.class)
            .addAnnotatedClass(CountryLanguage.class)
            .addProperties(properties)
            .buildSessionFactory();
    return sessionFactory;
}

Kita durung tekan lobak, mula implementasine awal saka klien lobak bakal tetep dadi rintisan kanggo saiki:

private void shutdown() {
    if (nonNull(sessionFactory)) {
        sessionFactory.close();
    }
    if (nonNull(redisClient)) {
        redisClient.shutdown();
    }
}

Pungkasan, kita bisa nulis cara kanggo narik kabeh kutha:

private List<City> fetchData(Main main) {
    try (Session session = main.sessionFactory.getCurrentSession()) {
        List<City> allCities = new ArrayList<>();
        session.beginTransaction();

        int totalCount = main.cityDAO.getTotalCount();
        int step = 500;
        for (int i = 0; i < totalCount; i += step) {
            allCities.addAll(main.cityDAO.getItems(i, step));
        }
        session.getTransaction().commit();
        return allCities;
    }
}

Fitur implementasine yaiku saben kita entuk 500 kutha. Iki perlu amarga ana watesan babagan jumlah data sing dikirim. Ya, ing kasus kita, kita ora bakal njaluk menyang wong-wong mau, amarga. kita duwe total 4079 kutha ing database. Nanging ing aplikasi produksi, nalika sampeyan kudu entuk akeh data, teknik iki asring digunakake.

Lan implementasine saka cara utama:

public static void main(String[] args) {
    Main main = new Main();
    List<City> allCities = main.fetchData(main);
    main.shutdown();
}

Saiki kita bisa mbukak aplikasi kita ing debug kanggo pisanan lan ndeleng cara kerjane (utawa ora bisa digunakake - ya, asring kedadeyan).

Kutha-kutha wis entuk. Saben kutha entuk negara, yen durung dikurangi saka database kanggo kutha liyane. Ayo kira-kira ngitung jumlah pitakon sing bakal dikirim Hibernate menyang database:

  • 1 panjalukan kanggo ngerteni jumlah total kutha (perlu ngulang luwih saka 500 kutha supaya ngerti kapan kudu mandheg).
  • 4079 / 500 = 9 panjalukan (dhaftar kutha).
  • Saben kutha entuk negara, yen durung dikurangi sadurunge. Amarga ana 239 negara ing basis data, iki bakal menehi 239 pitakon.

Total 249 requests. Lan kita uga ujar manawa bebarengan karo negara kita bakal langsung nampa set basa, yen ora ana pepeteng umume. Nanging isih akeh, mula kita ngapiki prilaku. Ayo dadi miwiti karo bayangan: apa sing kudu dilakoni, ngendi kanggo mbukak? Nanging serius - kok akeh sing njaluk. Yen sampeyan ndeleng log panyuwunan, kita ndeleng manawa saben negara dijaluk kanthi kapisah, mula solusi sing gampang pisanan: ayo padha njaluk kabeh negara bebarengan, amarga kita ngerti sadurunge yen kita butuh kabeh ing transaksi iki.

Ing metode fetchData (), sanalika sawise wiwitan transaksi, tambahake baris ing ngisor iki:

List<Country> countries = main.countryDAO.getAll(); 

We count panjalukan:

  • 1 - entuk kabeh negara
  • 239 - pitakon kanggo saben negara ibukutha
  • 1 - panjalukan kanggo nomer kutha
  • 9 - panjalukan kanggo dhaptar kutha

Total 250. Ide iki apik, nanging ora bisa. Masalahe yaiku negara duwe sesambungan karo ibukutha (kutha) @OneToOne. Lan pranala kasebut langsung dimuat kanthi gawan ( FetchType.EAGER). Ayo sijine FetchType.LAZY, amarga tho, kita bakal mbukak kabeh kutha mengko ing transaksi padha.

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "capital")
private City city;

Ibukutha ora dijaluk maneh kanthi kapisah, nanging jumlah panjaluk ora owah. Saiki, kanggo saben negara, dhaptar CountryLanguage dijaluk dening pitakon kapisah . Tegese, ana kemajuan, lan kita pindhah menyang arah sing bener. Yen sampeyan ngelingi, ceramah nyaranake solusi "gabungan njupuk" kanggo njaluk entitas kanthi data gumantung ing siji panyuwunan kanthi nambahake gabungan tambahan menyang panyuwunan kasebut. Ing CountryDAO , tulis ulang query HQL ing cara getAll()kanggo:

"select c from Country c join fetch c.languages" 

Bukak. Kita ndeleng log, ngitung panjaluk:

  • 1 - kabeh negara nganggo basa
  • 1 - nomer kutha
  • 9 - dhaptar kutha.

Total 11- kita sukses)) Yen sampeyan ora mung maca kabeh teks iki, nanging uga nyoba kanggo mbukak sawise saben langkah tuning aplikasi, sampeyan kudu malah visual akselerasi kabeh aplikasi kaping pirang-pirang.

Tulis metode transformasi data

Ayo nggawe paket com.codegym.redissing ditambahake 2 kelas: CityCountry (data kutha lan negara sing dumunung ing kutha iki) lan Basa (data babagan basa). Ing ngisor iki kabeh lapangan sing asring dijaluk "kanthi tugas" ing "panyuwunan rem".

package com.codegym.redis;

import com.codegym.domain.Continent;

import java.math.BigDecimal;
import java.util.Set;

public class CityCountry {
    private Integer id;

    private String name;

    private String district;

    private Integer population;

    private String countryCode;

    private String alternativeCountryCode;

    private String countryName;

    private Continent continent;

    private String countryRegion;

    private BigDecimal countrySurfaceArea;

    private Integer countryPopulation;

    private Set<Language> languages;

    //Getters and Setters omitted
}
package com.codegym.redis;

import java.math.BigDecimal;

public class Language {
    private String language;
    private Boolean isOfficial;
    private BigDecimal percentage;

    //Getters and Setters omitted
}

Ing cara utama, sawise entuk kabeh kutha, nambah baris

List<CityCountry>> preparedData = main.transformData(allCities); 

Lan ngleksanakake cara iki:

private List<CityCountry> transformData(List<City> cities) {
    return cities.stream().map(city -> {
        CityCountry res = new CityCountry();
        res.setId(city.getId());
        res.setName(city.getName());
        res.setPopulation(city.getPopulation());
        res.setDistrict(city.getDistrict());

        Country country = city.getCountry();
        res.setAlternativeCountryCode(country.getAlternativeCode());
        res.setContinent(country.getContinent());
        res.setCountryCode(country.getCode());
        res.setCountryName(country.getName());
        res.setCountryPopulation(country.getPopulation());
        res.setCountryRegion(country.getRegion());
        res.setCountrySurfaceArea(country.getSurfaceArea());
        Set<CountryLanguage> countryLanguages = country.getLanguages();
        Set<Language> languages = countryLanguages.stream().map(cl -> {
            Language language = new Language();
            language.setLanguage(cl.getLanguage());
            language.setOfficial(cl.getOfficial());
            language.setPercentage(cl.getPercentage());
            return language;
        }).collect(Collectors.toSet());
        res.setLanguages(languages);

        return res;
    }).collect(Collectors.toList());
}

Aku cara iki cukup panjelasan: kita mung nggawe entitas CityCountry lan isi karo data saka City , Country , CountryLanguage .

Jalanake server Redis minangka wadhah docker

Ana 2 pilihan ing kene. Yen sampeyan nindakake langkah opsional "instal redis-insight, deleng data sing disimpen ing Redis", banjur prentah kasebut kanggo sampeyan:

docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest 

Yen sampeyan mutusake kanggo ngliwati langkah iki, banjur mung:

docker run -d --name redis -p 6379:6379 redis:latest 

Bentenipun ing pilihan pisanan, port 8001 diterusake menyang mesin lokal, sing bisa nyambung karo klien eksternal kanggo ndeleng apa sing disimpen ing njero. Lan aku digunakake kanggo menehi jeneng migunani, mulane, redis-stackutawa redis.

Sawise diluncurake, sampeyan bisa ndeleng dhaptar kontaner sing mlaku. Kanggo nindakake iki, jalanake printah:

docker container ls 

Lan sampeyan bakal weruh kaya iki:

Yen sampeyan kudu golek sawetara printah, sampeyan bisa uga katon ing bantuan ing terminal (bantuan docker) utawa google "carane ..." (contone, docker carane mbusak wadhah mlaku).

Lan kita uga disebut initialization saka klien radish ing konstruktor Utama, nanging ora ngleksanakake cara dhewe. Tambah implementasine:

private RedisClient prepareRedisClient() {
    RedisClient redisClient = RedisClient.create(RedisURI.create("localhost", 6379));
    try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
        System.out.println("\nConnected to Redis\n");
    }
    return redisClient;
}

sout ditambahake kanggo tujuan pendidikan supaya ing log peluncuran sampeyan bisa ndeleng manawa kabeh OK lan sambungan liwat klien lobak liwati tanpa kesalahan.

Tulis data menyang Redis

Tambah telpon menyang cara utama

main.pushToRedis(preparedData); 

Kanthi implementasine metode iki:

private void pushToRedis(List<CityCountry> data) {
    try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
        RedisStringCommands<String, String> sync = connection.sync();
        for (CityCountry cityCountry : data) {
            try {
                sync.set(String.valueOf(cityCountry.getId()), mapper.writeValueAsString(cityCountry));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }

    }
}

Ing kene, sambungan sinkron dibukak karo klien lobak, lan saben obyek saka jinis CityCountry ditulis ing lobak. Wiwit lobak minangka toko kunci-nilai String , kunci (id kutha) diowahi dadi senar. Lan regane uga kanggo senar, nanging nggunakake ObjectMapper ing format JSON.

Iku tetep kanggo mbukak lan mriksa sing ora ana kasalahan ing log. Kabeh bisa.

Instal redis-insight, deleng data sing disimpen ing Redis (opsional)

Download redis-inight saka link lan nginstal. Sawise miwiti, langsung nuduhake conto lobak ing wadhah docker:

Yen sampeyan mlebu, kita bakal weruh dhaptar kabeh tombol:

Lan sampeyan bisa pindhah menyang sembarang tombol kanggo ndeleng data apa sing disimpen ing:

Tulis cara kanggo njupuk data saka Redis

Kanggo tes, kita nggunakake tes ing ngisor iki: entuk 10 cathetan CityCountry. Saben karo request kapisah, nanging ing siji sambungan.

Data saka lobak bisa dipikolehi liwat klien lobak. Kanggo nindakake iki, ayo nulis metode sing njupuk dhaptar id kanggo entuk.

private void testRedisData(List<Integer> ids) {
    try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
        RedisStringCommands<String, String> sync = connection.sync();
        for (Integer id : ids) {
            String value = sync.get(String.valueOf(id));
            try {
                mapper.readValue(value, CityCountry.class);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
    }
}

Implementasine, aku, intuisi: kita mbukak sambungan sinkron, lan kanggo saben id kita entuk JSON String , sing diowahi dadi obyek saka jinis CityCountry sing dibutuhake .

Tulis cara kanggo njupuk data saka MySQL

Ing kelas CityDAO , tambahake metode getById(Integer id)sing bakal entuk kutha bebarengan karo negara:

public City getById(Integer id) {
    Query<City> query = sessionFactory.getCurrentSession().createQuery("select c from City c join fetch c.country where c.id = :ID", City.class);
    query.setParameter("ID", id);
    return query.getSingleResult();
}

Kanthi analogi karo paragraf sadurunge, ayo nambah metode sing padha kanggo MySQL menyang kelas Utama:

private void testMysqlData(List<Integer> ids) {
    try (Session session = sessionFactory.getCurrentSession()) {
        session.beginTransaction();
        for (Integer id : ids) {
            City city = cityDAO.getById(id);
            Set<CountryLanguage> languages = city.getCountry().getLanguages();
        }
        session.getTransaction().commit();
    }
}

Saka fitur kasebut, kanggo mesthekake entuk obyek lengkap (tanpa stub proxy), kita kanthi jelas njaluk dhaptar basa saka negara kasebut.

Bandhingake kacepetan njupuk data sing padha saka MySQL lan Redis

Ing kene aku bakal langsung menehi kode metode utama, lan asil sing dipikolehi ing komputer lokal.

public static void main(String[] args) {
    Main main = new Main();
    List<City> allCities = main.fetchData(main);
    List<CityCountry> preparedData = main.transformData(allCities);
    main.pushToRedis(preparedData);

    // close the current session in order to make a query to the database for sure, and not to pull data from the cache
    main.sessionFactory.getCurrentSession().close();

    //choose random 10 id cities
    //since we did not handle invalid situations, use the existing id in the database
    List<Integer> ids = List.of(3, 2545, 123, 4, 189, 89, 3458, 1189, 10, 102);

    long startRedis = System.currentTimeMillis();
    main.testRedisData(ids);
    long stopRedis = System.currentTimeMillis();

    long startMysql = System.currentTimeMillis();
    main.testMysqlData(ids);
    long stopMysql = System.currentTimeMillis();

    System.out.printf("%s:\t%d ms\n", "Redis", (stopRedis - startRedis));
    System.out.printf("%s:\t%d ms\n", "MySQL", (stopMysql - startMysql));

    main.shutdown();
}

Nalika nyoba, ana fitur - data saka MySQL mung diwaca, saengga ora bisa diwiwiti maneh ing antarane peluncuran aplikasi kita. Lan ing Redis padha ditulis.

Senajan nalika sampeyan nyoba kanggo nambah duplikat kanggo tombol padha, data mung bakal dianyari, Aku Rekomendasi sing mbukak printah kanggo mungkasi wadhah docker stop redis-stacklan mbusak wadhah antarane aplikasi diluncurake ing terminal docker rm redis-stack. Sawisé iku, mundhakaken wadhah karo lobak maneh docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latestlan mung sawise aplikasi kita.

Iki asil tesku:

In total, kita wis ngrambah Tambah ing kinerja respon kanggo request "frequency rem" dening siji lan setengah kaping. Lan iki njupuk menyang akun kasunyatan sing ing testing kita digunakake ora deserialization paling cepet liwat ObjectMapper . Yen sampeyan ngganti menyang GSON, paling kamungkinan, sampeyan bisa "menang" sethitik liyane wektu.

Ing wektu iki, aku ngelingi lelucon babagan programmer lan wektu: maca lan mikir babagan carane nulis lan ngoptimalake kode sampeyan.