కోడ్‌జిమ్/జావా కోర్సు/All lectures for TE purposes/సంబంధిత ప్రాజెక్ట్: SQL, JDBC మరియు హైబర్నేట్

సంబంధిత ప్రాజెక్ట్: SQL, JDBC మరియు హైబర్నేట్

అందుబాటులో ఉంది

ఈ రోజు మనం నాల్గవ JRU మాడ్యూల్‌పై తుది ప్రాజెక్ట్ చేస్తాము. అది ఏమి అవుతుంది? విభిన్న సాంకేతికతలతో పని చేయడానికి ప్రయత్నిద్దాం: MySQL, Hibernate, Redis, Docker. ఇప్పుడు మరింత విషయం.

విధి: మేము స్కీమా (దేశం-నగరం, దేశం వారీగా భాష)తో సంబంధిత MySQL డేటాబేస్‌ని కలిగి ఉన్నాము. మరియు నగరం యొక్క తరచుగా అభ్యర్థన ఉంది, ఇది నెమ్మదిస్తుంది. మేము ఒక పరిష్కారాన్ని కనుగొన్నాము - తరచుగా అభ్యర్థించే మొత్తం డేటాను Redisకి తరలించడానికి (కీ-విలువ రకం యొక్క మెమరీ నిల్వలో).

మరియు మాకు MySQLలో నిల్వ చేయబడిన మొత్తం డేటా అవసరం లేదు, కానీ ఎంచుకున్న ఫీల్డ్‌ల సెట్ మాత్రమే. ప్రాజెక్ట్ ట్యుటోరియల్ రూపంలో ఉంటుంది. అంటే, ఇక్కడ మేము సమస్యను లేవనెత్తాము మరియు వెంటనే దాన్ని పరిష్కరిస్తాము.

కాబట్టి, మనకు అవసరమైన సాఫ్ట్‌వేర్‌తో ప్రారంభిద్దాం:

  1. IDEA అల్టిమేట్ (ఎవరు కీ అయిపోయింది - రోమన్ ఇన్ ది స్లాక్‌కి వ్రాయండి)
  2. వర్క్‌బెంచ్ (లేదా MySQL కోసం ఏదైనా ఇతర క్లయింట్)
  3. డాకర్
  4. redis-insight - ఐచ్ఛికం

మా కార్యాచరణ ప్రణాళిక:

  1. డాకర్‌ను సెటప్ చేయండి (నేను దీన్ని ట్యుటోరియల్‌లో చేయను, ఎందుకంటే ప్రతి OS దాని స్వంత లక్షణాలను కలిగి ఉంటుంది మరియు "విండోస్‌లో డాకర్‌ను ఎలా ఇన్‌స్టాల్ చేయాలి" వంటి ప్రశ్నలకు ఇంటర్నెట్‌లో చాలా సమాధానాలు ఉన్నాయి), ప్రతిదీ పని చేస్తుందో లేదో తనిఖీ చేయండి.
  2. MySQL సర్వర్‌ను డాకర్ కంటైనర్‌గా అమలు చేయండి.
  3. డంప్‌ని విస్తరించండి .
  4. ఐడియాలో ప్రాజెక్ట్‌ను సృష్టించండి, మావెన్ డిపెండెన్సీలను జోడించండి.
  5. లేయర్ డొమైన్ చేయండి.
  6. MySQL నుండి మొత్తం డేటాను పొందడానికి ఒక పద్ధతిని వ్రాయండి.
  7. డేటా పరివర్తన పద్ధతిని వ్రాయండి (రెడిస్‌లో మేము తరచుగా అభ్యర్థించే డేటాను మాత్రమే వ్రాస్తాము).
  8. Redis సర్వర్‌ను డాకర్ కంటైనర్‌గా అమలు చేయండి.
  9. Redisకి డేటాను వ్రాయండి.
  10. ఐచ్ఛికం: redis-inightని ఇన్‌స్టాల్ చేయండి, Redisలో నిల్వ చేయబడిన డేటాను చూడండి.
  11. Redis నుండి డేటా పొందడానికి ఒక పద్ధతిని వ్రాయండి.
  12. MySQL నుండి డేటాను పొందడానికి ఒక పద్ధతిని వ్రాయండి.
  13. MySQL మరియు Redis నుండి ఒకే డేటాను పొందే వేగాన్ని సరిపోల్చండి.

డాకర్ సెటప్

డాకర్ అనేది అప్లికేషన్‌లను డెవలప్ చేయడానికి, డెలివరీ చేయడానికి మరియు ఆపరేటింగ్ చేయడానికి ఒక ఓపెన్ ప్లాట్‌ఫారమ్. స్థానిక మెషీన్‌లో Redisని ఇన్‌స్టాల్ చేయడానికి మరియు కాన్ఫిగర్ చేయకుండా, రెడీమేడ్ ఇమేజ్‌ని ఉపయోగించడానికి మేము దీన్ని ఉపయోగిస్తాము. మీరు ఇక్కడ డాకర్ గురించి మరింత చదవవచ్చు లేదా ఇక్కడ చూడవచ్చు . మీకు డాకర్ గురించి తెలియకపోతే, రెండవ లింక్‌ని చూడాలని నేను సిఫార్సు చేస్తున్నాను.

మీరు డాకర్ ఇన్‌స్టాల్ చేసి కాన్ఫిగర్ చేశారని నిర్ధారించుకోవడానికి, ఆదేశాన్ని అమలు చేయండి:docker -v

ప్రతిదీ సరిగ్గా ఉంటే, మీరు డాకర్ వెర్షన్‌ను చూస్తారు

MySQL సర్వర్‌ను డాకర్ కంటైనర్‌గా అమలు చేయండి

MySQL మరియు Redis నుండి డేటా తిరిగి వచ్చే సమయాన్ని సరిపోల్చడానికి, మేము డాకర్‌లో MySQLని కూడా ఉపయోగిస్తాము. పవర్‌షెల్‌లో (లేదా మీరు విండోస్‌ని ఉపయోగించకపోతే మరొక కన్సోల్ టెర్మినల్), ఆదేశాన్ని అమలు చేయండి:

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

ఈ ఆదేశంతో మనం ఏమి చేస్తున్నామో పరిశీలించండి:

  • docker run– చిత్రాన్ని ప్రారంభించడం (మరియు డౌన్‌లోడ్ చేయడం, ఇది ఇంకా స్థానిక మెషీన్‌కు డౌన్‌లోడ్ చేయబడకపోతే). ప్రయోగ ఫలితంగా, మేము నడుస్తున్న కంటైనర్ను పొందుతాము.
  • --name mysql- mysql కంటైనర్ పేరును సెట్ చేయండి.
  • -d- మీరు ఈ కంటైనర్ ప్రారంభించబడిన టెర్మినల్ విండోను మూసివేసినప్పటికీ, కంటైనర్ పని చేయడం కొనసాగించాలని చెప్పే ఫ్లాగ్.
  • -p 3306:3306- పోర్టులను నిర్దేశిస్తుంది. పెద్దప్రేగు ముందు - స్థానిక యంత్రంలో పోర్ట్, పెద్దప్రేగు తర్వాత - కంటైనర్లో పోర్ట్.
  • -e MYSQL_ROOT_PASSWORD=root– ఎన్విరాన్మెంట్ వేరియబుల్ MYSQL_ROOT_PASSWORDని విలువ రూట్‌తో కంటైనర్‌కు పంపడం. mysql/ ఇమేజ్‌కి ప్రత్యేకంగా ఫ్లాగ్ చేయండి
  • --restart unless-stopped- ప్రవర్తన విధానాన్ని సెట్ చేయడం (కంటెయినర్ మూసి ఉన్నప్పుడు పునఃప్రారంభించాలా వద్దా). ఆపివేయబడని విలువ అంటే కంటైనర్ ఆపివేయబడినప్పుడు తప్ప, ఎల్లప్పుడూ పునఃప్రారంభించబడుతుందని అర్థం /
  • -v mysql:/var/lib/mysql - వాల్యూమ్ జోడించండి (సమాచారాన్ని నిల్వ చేయడానికి చిత్రం).
  • mysql:8 - చిత్రం పేరు మరియు దాని వెర్షన్.

టెర్మినల్‌లో ఆదేశాన్ని అమలు చేసిన తర్వాత, డాకర్ చిత్రం యొక్క అన్ని లేయర్‌లను డౌన్‌లోడ్ చేస్తుంది మరియు కంటైనర్‌ను ప్రారంభిస్తుంది:

ముఖ్యమైన గమనిక: మీరు MySQLని మీ స్థానిక కంప్యూటర్‌లో సేవగా ఇన్‌స్టాల్ చేసి, అది రన్ అవుతుంటే, మీరు ప్రారంభ కమాండ్‌లో వేరే పోర్ట్‌ను పేర్కొనాలి లేదా ఈ నడుస్తున్న సేవను నిలిపివేయాలి.

డంప్‌ని విస్తరించండి

డంప్‌ను విస్తరించడానికి, మీరు వర్క్‌బెంచ్ నుండి డేటాబేస్కు కొత్త కనెక్షన్‌ని సృష్టించాలి, ఇక్కడ మీరు పారామితులను పేర్కొనండి. నేను డిఫాల్ట్ పోర్ట్ (3306)ని ఉపయోగించాను, వినియోగదారు పేరును (డిఫాల్ట్‌గా రూట్) మార్చలేదు మరియు రూట్ యూజర్ (రూట్) కోసం పాస్‌వర్డ్‌ను సెట్ చేసాను.

వర్క్‌బెంచ్‌లో, చేయండి Data Import/Restoreమరియు ఎంచుకోండి Import from Self Contained File. మీరు డంప్‌ని ఫైల్‌గా ఎక్కడ డౌన్‌లోడ్ చేసారో పేర్కొనండి . మీరు స్కీమాను ముందుగా సృష్టించాల్సిన అవసరం లేదు - దాని సృష్టి డంప్ ఫైల్‌లో చేర్చబడింది. విజయవంతమైన దిగుమతి తర్వాత, మీరు మూడు పట్టికలతో ప్రపంచ స్కీమాను కలిగి ఉంటారు:

  1. నగరం నగరాల పట్టిక.
  2. దేశం - దేశం పట్టిక.
  3. దేశం_భాష - దేశంలోని జనాభాలో ఎంత శాతం మంది నిర్దిష్ట భాష మాట్లాడుతున్నారో సూచించే పట్టిక.

కంటైనర్‌ను ప్రారంభించేటప్పుడు మేము వాల్యూమ్‌ను ఉపయోగించాము కాబట్టి, mysql కంటైనర్‌ను ఆపివేసి, తొలగించిన తర్వాత మరియు ప్రారంభ ఆదేశాన్ని ( docker run --name mysql -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root --restart unless-stopped -v mysql:/var/lib/mysql mysql:8) మళ్లీ అమలు చేసిన తర్వాత, డంప్‌ను మళ్లీ అమలు చేయవలసిన అవసరం ఉండదు - ఇది ఇప్పటికే వాల్యూమ్‌లో అమలు చేయబడింది.

ఐడియాలో ప్రాజెక్ట్‌ను సృష్టించండి, మావెన్ డిపెండెన్సీలను జోడించండి

ఐడియాలో ప్రాజెక్ట్‌ను ఎలా సృష్టించాలో మీకు ఇప్పటికే తెలుసు - ఇది నేటి ప్రాజెక్ట్‌లో సులభమైన పాయింట్.

పోమ్ ఫైల్‌కు డిపెండెన్సీలను జోడించండి:


<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> 

మొదటి మూడు డిపెండెన్సీలు మీకు చాలా కాలంగా సుపరిచితం.

lettuce-coreRedisతో పని చేయడానికి అందుబాటులో ఉన్న జావా క్లయింట్‌లలో ఒకటి.

jackson-databind– ఆబ్జెక్ట్‌మ్యాపర్‌ని ఉపయోగించడం కోసం డిపెండెన్సీ (రెడిస్‌లో నిల్వ కోసం డేటాను మార్చడానికి (స్ట్రింగ్ రకం కీ-విలువ)).

అలాగే వనరుల ఫోల్డర్‌లో (src/main/resources) హైబర్నేట్ అమలు చేసే పారామితులతో అభ్యర్థనలను వీక్షించడానికి spy.propertiesని జోడించండి. ఫైల్ కంటెంట్‌లు:

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 

లేయర్ డొమైన్ చేయండి

com.codegym.domain ప్యాకేజీని సృష్టించండి

ఐడియాలో టేబుల్ స్ట్రక్చర్‌ని ఉపయోగించడానికి ఒక ఎంటిటీలో టేబుల్‌లను మ్యాపింగ్ చేసేటప్పుడు ఇది నాకు సౌకర్యవంతంగా ఉంటుంది, కాబట్టి ఐడియాలో డేటాబేస్ కనెక్షన్‌ని జోడిద్దాం.

ఈ క్రమంలో ఎంటిటీలను సృష్టించమని నేను సూచిస్తున్నాను:

  • దేశం
  • నగరం
  • దేశభాష

మ్యాపింగ్‌ను మీరే నిర్వహించడం మంచిది.

దేశం తరగతి కోడ్:

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

}

కోడ్‌లో 3 ఆసక్తికరమైన అంశాలు ఉన్నాయి.

మొదటిది కాంటినెంట్ ఎనామ్ , ఇది ఆర్డినల్ విలువలుగా డేటాబేస్‌లో నిల్వ చేయబడుతుంది. దేశం పట్టిక నిర్మాణంలో, ఖండం ఫీల్డ్‌కు వ్యాఖ్యలలో, ఏ ఖండానికి ఏ సంఖ్యా విలువ అనుగుణంగా ఉందో మీరు చూడవచ్చు.

package com.codegym.domain;

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

రెండవ పాయింట్ ఎంటిటీల సమితిCountryLanguage . @OneToManyఈ మాడ్యూల్ యొక్క రెండవ డ్రాఫ్ట్‌లో లేని లింక్ ఇక్కడ ఉంది . డిఫాల్ట్‌గా, దేశం ఎంటిటీని అభ్యర్థిస్తున్నప్పుడు హైబర్నేట్ ఈ సెట్ విలువను తీసివేయదు. కానీ మేము కాషింగ్ కోసం రిలేషనల్ డేటాబేస్ నుండి అన్ని విలువలను తీసివేయాలి కాబట్టి, ది FetchType.EAGER.

మూడవది నగర క్షేత్రం . కమ్యూనికేషన్ @OneToOne- ప్రతిదీ తెలిసిన మరియు అర్థమయ్యేలా. కానీ, డేటాబేస్‌లోని ఫారిన్ కీ స్ట్రక్చర్‌ను పరిశీలిస్తే, దేశానికి (దేశం) రాజధానికి (నగరం) లింక్ ఉందని, నగరం (నగరం) దేశం (దేశం)కి లింక్ ఉందని మనం చూస్తాము. ఒక చక్రీయ సంబంధం ఉంది.

మేము దీనితో ఇంకా ఏమీ చేయము, కానీ "MySQL నుండి మొత్తం డేటాను పొందడానికి ఒక పద్ధతిని వ్రాయండి" ఐటెమ్‌కు వచ్చినప్పుడు, హైబర్నేట్ ఏ ప్రశ్నలను అమలు చేస్తుందో చూద్దాం, వాటి సంఖ్యను చూడండి మరియు ఈ అంశాన్ని గుర్తుంచుకోండి. హైబర్నేట్

సిటీ క్లాస్ కోడ్:

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

}

దేశభాష తరగతి కోడ్:

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
}

MySQL నుండి మొత్తం డేటాను పొందడానికి ఒక పద్ధతిని వ్రాయండి

ప్రధాన తరగతిలో, మేము ఫీల్డ్‌లను ప్రకటిస్తాము:

private final SessionFactory sessionFactory;
private final RedisClient redisClient;

private final ObjectMapper mapper;

private final CityDAO cityDAO;
private final CountryDAO countryDAO;

మరియు వాటిని ప్రధాన తరగతి యొక్క కన్స్ట్రక్టర్‌లో ప్రారంభించండి:

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

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

మీరు గమనిస్తే, తగినంత పద్ధతులు మరియు తరగతులు లేవు - వాటిని వ్రాస్దాం.

com.codegym.dao ప్యాకేజీని ప్రకటించి, దానికి 2 తరగతులను జోడించండి:

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());
    }
}

ఇప్పుడు మీరు ఈ 2 తరగతులను మెయిన్‌లోకి దిగుమతి చేసుకోవచ్చు. ఇప్పటికీ రెండు పద్ధతులు లేవు:

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;
}

మేము ఇంకా ముల్లంగిని చేరుకోలేదు, కాబట్టి ముల్లంగి క్లయింట్ యొక్క ప్రారంభీకరణ అమలు ప్రస్తుతానికి ఒక స్టబ్‌గా మిగిలిపోయింది:

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

చివరగా, మేము అన్ని నగరాలను తీసివేసే పద్ధతిని వ్రాయవచ్చు:

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;
    }
}

అమలు లక్షణం ఏమిటంటే మనం ఒక్కొక్కటి 500 నగరాలను పొందుతాము. ప్రసారం చేయబడిన డేటా మొత్తంపై పరిమితులు ఉన్నందున ఇది అవసరం. అవును, మా విషయంలో, మేము వాటిని పొందలేము, ఎందుకంటే. డేటాబేస్‌లో మాకు మొత్తం 4079 నగరాలు ఉన్నాయి. కానీ ఉత్పత్తి అనువర్తనాల్లో, మీరు చాలా డేటాను పొందవలసి వచ్చినప్పుడు, ఈ సాంకేతికత తరచుగా ఉపయోగించబడుతుంది.

మరియు ప్రధాన పద్ధతి యొక్క అమలు:

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

ఇప్పుడు మనం మొదటిసారిగా డీబగ్‌లో మా అప్లికేషన్‌ను అమలు చేయవచ్చు మరియు అది ఎలా పనిచేస్తుందో చూడవచ్చు (లేదా పని చేయదు - అవును, ఇది తరచుగా జరుగుతుంది).

నగరాలు పొందుతున్నాయి. ప్రతి నగరం ఒక దేశాన్ని పొందుతుంది, అది ఇంతకు ముందు మరొక నగరం కోసం డేటాబేస్ నుండి తీసివేయబడకపోతే. డేటాబేస్‌కు హైబర్నేట్ ఎన్ని ప్రశ్నలను పంపుతుందో స్థూలంగా లెక్కిద్దాం:

  • మొత్తం నగరాల సంఖ్యను తెలుసుకోవడానికి 1 అభ్యర్థన (ఎప్పుడు ఆపివేయాలో తెలుసుకోవడానికి 500 కంటే ఎక్కువ నగరాలను పునరావృతం చేయాలి).
  • 4079 / 500 = 9 అభ్యర్థనలు (నగరాల జాబితా).
  • ప్రతి నగరానికి ఒక దేశం వస్తుంది, అది ముందుగా తీసివేయబడకపోతే. డేటాబేస్‌లో 239 దేశాలు ఉన్నందున, ఇది మాకు 239 ప్రశ్నలను ఇస్తుంది.

Total 249 requests. మరియు మేము దేశంతో కలిసి వెంటనే భాషల సమితిని అందుకుంటామని, లేకపోతే సాధారణంగా చీకటి ఉంటుందని కూడా చెప్పాము. కానీ ఇది ఇంకా చాలా ఉంది, కాబట్టి ప్రవర్తనను కొంచెం సర్దుబాటు చేద్దాం. ప్రతిబింబాలతో ప్రారంభిద్దాం: ఏమి చేయాలి, ఎక్కడ అమలు చేయాలి? కానీ తీవ్రంగా - ఎందుకు చాలా అభ్యర్థనలు ఉన్నాయి. మీరు అభ్యర్థన లాగ్‌ని చూస్తే, ప్రతి దేశం విడివిడిగా అభ్యర్థించబడుతుందని మేము చూస్తాము, కాబట్టి మొదటి సాధారణ పరిష్కారం: అన్ని దేశాలను కలిసి అభ్యర్థించండి, ఎందుకంటే ఈ లావాదేవీలో అవన్నీ మనకు అవసరమని మాకు ముందుగానే తెలుసు.

FetchData() పద్ధతిలో, లావాదేవీ ప్రారంభమైన వెంటనే, కింది పంక్తిని జోడించండి:

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

మేము అభ్యర్థనలను లెక్కిస్తాము:

  • 1 - అన్ని దేశాలను పొందండి
  • 239 - దాని రాజధాని యొక్క ప్రతి దేశం కోసం ప్రశ్న
  • 1 - నగరాల సంఖ్య కోసం అభ్యర్థన
  • 9 - నగరాల జాబితాల కోసం అభ్యర్థన

Total 250. ఆలోచన బాగుంది, కానీ అది ఫలించలేదు. సమస్య ఏమిటంటే దేశానికి రాజధాని (నగరం)తో సంబంధం ఉంది @OneToOne. మరియు అటువంటి లింక్ డిఫాల్ట్‌గా వెంటనే లోడ్ చేయబడుతుంది ( FetchType.EAGER). లెట్ FetchType.LAZY, ఎందుకంటే ఏమైనప్పటికీ, మేము అదే లావాదేవీలో అన్ని నగరాలను తర్వాత లోడ్ చేస్తాము.

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

క్యాపిటల్స్ ఇకపై విడిగా అభ్యర్థించబడవు, కానీ అభ్యర్థనల సంఖ్య మారలేదు. ఇప్పుడు, ప్రతి దేశం కోసం, దేశభాష జాబితా ప్రత్యేక ప్రశ్న ద్వారా అభ్యర్థించబడుతుంది . అంటే, పురోగతి ఉంది, మరియు మేము సరైన దిశలో కదులుతున్నాము. మీకు గుర్తున్నట్లయితే, అభ్యర్థనకు అదనపు చేరికను జోడించడం ద్వారా ఒక అభ్యర్థనలో డిపెండెంట్ డేటాతో ఎంటిటీని అభ్యర్థించడానికి ఉపన్యాసాలు "జాయిన్ ఫెచ్" పరిష్కారాన్ని సూచించాయి. CountryDAO లో , HQL ప్రశ్నను ఈ పద్ధతిలో తిరిగి వ్రాయండి getAll():

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

ప్రారంభించండి. మేము లాగ్‌ను పరిశీలిస్తాము, అభ్యర్థనలను లెక్కించండి:

  • 1 - భాషలు ఉన్న అన్ని దేశాలు
  • 1 - నగరాల సంఖ్య
  • 9 - నగరాల జాబితాలు.

Total 11- మేము విజయం సాధించాము)) మీరు ఈ వచనాన్ని చదవడమే కాకుండా, అప్లికేషన్‌ను ట్యూన్ చేసే ప్రతి దశ తర్వాత దాన్ని అమలు చేయడానికి ప్రయత్నించినట్లయితే, మీరు మొత్తం అప్లికేషన్ యొక్క త్వరణాన్ని చాలాసార్లు గమనించాలి.

డేటా పరివర్తన పద్ధతిని వ్రాయండి

com.codegym.redisమేము 2 తరగతులను జోడించే ప్యాకేజీని సృష్టిద్దాం : CityCountry (నగరం మరియు ఈ నగరం ఉన్న దేశంపై డేటా) మరియు భాష (భాషపై డేటా). "బ్రేకింగ్ అభ్యర్థన"లో తరచుగా "టాస్క్ ద్వారా" అభ్యర్థించే అన్ని ఫీల్డ్‌లు ఇక్కడ ఉన్నాయి.

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
}

ప్రధాన పద్ధతిలో, అన్ని నగరాలను పొందిన తర్వాత, లైన్ జోడించండి

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

మరియు ఈ పద్ధతిని అమలు చేయండి:

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());
}

ఈ పద్ధతి స్వీయ-వివరణాత్మకమైనదని నేను భావిస్తున్నాను: మేము సిటీ కంట్రీ ఎంటిటీని సృష్టించి, నగరం , దేశం , కంట్రీ లాంగ్వేజ్ నుండి డేటాతో నింపుతాము .

Redis సర్వర్‌ని డాకర్ కంటైనర్‌గా అమలు చేయండి

ఇక్కడ 2 ఎంపికలు ఉన్నాయి. మీరు ఐచ్ఛిక దశను చేస్తే, "redis-insightని ఇన్‌స్టాల్ చేయండి, Redisలో నిల్వ చేయబడిన డేటాను చూడండి", అప్పుడు ఆదేశం మీ కోసం:

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

మీరు ఈ దశను దాటవేయాలని నిర్ణయించుకుంటే, కేవలం:

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

తేడా ఏమిటంటే, మొదటి ఎంపికలో, పోర్ట్ 8001 లోకల్ మెషీన్‌కు ఫార్వార్డ్ చేయబడింది, దానిలో మీరు లోపల నిల్వ చేయబడిన వాటిని చూడటానికి బాహ్య క్లయింట్‌తో కనెక్ట్ చేయవచ్చు. మరియు నేను అర్థవంతమైన పేర్లను ఇచ్చాను, కాబట్టి, redis-stackలేదా redis.

ప్రారంభించిన తర్వాత, మీరు నడుస్తున్న కంటైనర్ల జాబితాను చూడవచ్చు. దీన్ని చేయడానికి, ఆదేశాన్ని అమలు చేయండి:

docker container ls 

మరియు మీరు ఇలాంటివి చూస్తారు:

మీరు కొంత ఆదేశాన్ని కనుగొనవలసి ఉంటే, మీరు టెర్మినల్‌లో (డాకర్ సహాయం) లేదా Google "ఎలా ..." (ఉదాహరణకు, డాకర్ రన్నింగ్ కంటైనర్‌ను ఎలా తొలగించాలి)లో సహాయాన్ని చూడవచ్చు.

మరియు మేము మెయిన్ కన్స్ట్రక్టర్‌లో ముల్లంగి క్లయింట్ యొక్క ప్రారంభాన్ని కూడా పిలిచాము, కానీ పద్ధతిని అమలు చేయలేదు. అమలును జోడించండి:

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;
}

సౌట్ విద్యా ప్రయోజనాల కోసం జోడించబడింది, తద్వారా లాంచ్ లాగ్‌లో మీరు ప్రతిదీ సరిగ్గా ఉందని మరియు ముల్లంగి క్లయింట్ ద్వారా కనెక్షన్ లోపాలు లేకుండా ఆమోదించబడిందని చూడవచ్చు.

Redisకి డేటాను వ్రాయండి

ప్రధాన పద్ధతికి కాల్‌ని జోడించండి

main.pushToRedis(preparedData); 

ఈ పద్ధతి అమలుతో:

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();
            }
        }

    }
}

ఇక్కడ, ముల్లంగి క్లయింట్‌తో సింక్రోనస్ కనెక్షన్ తెరవబడుతుంది మరియు వరుసగా సిటీకంట్రీ రకంలోని ప్రతి వస్తువు ముల్లంగికి వ్రాయబడుతుంది. ముల్లంగి స్ట్రింగ్ కీ-వాల్యూ స్టోర్ అయినందున , కీ (సిటీ ఐడి) స్ట్రింగ్‌గా మార్చబడుతుంది. మరియు విలువ స్ట్రింగ్‌కు కూడా ఉంటుంది, కానీ JSON ఆకృతిలో ObjectMapperని ఉపయోగిస్తుంది.

లాగ్‌లో లోపాలు లేవని అమలు చేయడానికి మరియు తనిఖీ చేయడానికి ఇది మిగిలి ఉంది. అంతా పనిచేసింది.

redis-inightని ఇన్‌స్టాల్ చేయండి, Redisలో నిల్వ చేయబడిన డేటాను చూడండి (ఐచ్ఛికం)

లింక్ నుండి redis-inightని డౌన్‌లోడ్ చేసి , ఇన్‌స్టాల్ చేయండి. ప్రారంభించిన తర్వాత, ఇది వెంటనే డాకర్ కంటైనర్‌లో మా ముల్లంగి ఉదాహరణను చూపుతుంది:

మీరు లాగిన్ అయినట్లయితే, మేము అన్ని కీల జాబితాను చూస్తాము:

మరియు దానిపై ఏ డేటా నిల్వ చేయబడిందో చూడటానికి మీరు ఏదైనా కీకి వెళ్లవచ్చు:

Redis నుండి డేటా పొందడానికి ఒక పద్ధతిని వ్రాయండి

పరీక్ష కోసం, మేము క్రింది పరీక్షను ఉపయోగిస్తాము: మేము 10 సిటీ కంట్రీ రికార్డ్‌లను పొందుతాము. ప్రతి ఒక్కటి ప్రత్యేక అభ్యర్థనతో, కానీ ఒక కనెక్షన్‌లో.

ముల్లంగి నుండి డేటాను మా ముల్లంగి క్లయింట్ ద్వారా పొందవచ్చు. దీన్ని చేయడానికి, idల జాబితాను పొందే పద్ధతిని వ్రాస్దాం.

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();
            }
        }
    }
}

అమలు, నేను అనుకుంటున్నాను, స్పష్టమైనది: మేము సింక్రోనస్ కనెక్షన్‌ని తెరుస్తాము మరియు ప్రతి id కి మనం JSON స్ట్రింగ్‌ను పొందుతాము , దానిని మనకు అవసరమైన CityCountry రకం యొక్క వస్తువుగా మారుస్తాము .

MySQL నుండి డేటాను పొందడానికి ఒక పద్ధతిని వ్రాయండి

CityDAO తరగతిలో , getById(Integer id)దేశంతో పాటు నగరాన్ని పొందే పద్ధతిని జోడించండి :

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();
}

మునుపటి పేరాతో సారూప్యతతో, MySQL కోసం ఇదే పద్ధతిని ప్రధాన తరగతికి జోడిద్దాం:

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();
    }
}

ఫీచర్‌లలో, పూర్తి ఆబ్జెక్ట్‌ను (ప్రాక్సీ స్టబ్‌లు లేకుండా) పొందడం కోసం, మేము దేశం నుండి భాషల జాబితాను స్పష్టంగా అభ్యర్థిస్తాము.

MySQL మరియు Redis నుండి ఒకే డేటాను పొందే వేగాన్ని సరిపోల్చండి

ఇక్కడ నేను వెంటనే ప్రధాన పద్ధతి యొక్క కోడ్‌ను మరియు నా స్థానిక కంప్యూటర్‌లో పొందిన ఫలితాన్ని ఇస్తాను.

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();
}

పరీక్షిస్తున్నప్పుడు, ఒక ఫీచర్ ఉంది - MySQL నుండి డేటా మాత్రమే చదవబడుతుంది, కనుక ఇది మా అప్లికేషన్ లాంచ్‌ల మధ్య పునఃప్రారంభించబడదు. మరియు రెడిస్‌లో అవి వ్రాయబడ్డాయి.

మీరు అదే కీకి నకిలీని జోడించడానికి ప్రయత్నించినప్పుడు, డేటా కేవలం నవీకరించబడుతుంది, టెర్మినల్‌లోని అప్లికేషన్ లాంచ్‌ల మధ్య కంటైనర్‌ను ఆపివేయడానికి docker stop redis-stackమరియు కంటైనర్‌ను తొలగించడానికి మీరు ఆదేశాలను అమలు చేయాలని నేను సిఫార్సు చేస్తున్నాను docker rm redis-stack. ఆ తరువాత, మళ్లీ ముల్లంగితో కంటైనర్ను పెంచండి docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latestమరియు ఆ తర్వాత మాత్రమే మా అప్లికేషన్ను అమలు చేయండి.

నా పరీక్ష ఫలితాలు ఇక్కడ ఉన్నాయి:

మొత్తంగా, మేము "బ్రేకింగ్ తరచుగా" అభ్యర్థనకు ప్రతిస్పందన పనితీరులో ఒకటిన్నర రెట్లు పెరుగుదలను సాధించాము. మరియు పరీక్షలో మేము ObjectMapper ద్వారా వేగవంతమైన డీరియలైజేషన్‌ను ఉపయోగించలేదనే వాస్తవాన్ని ఇది పరిగణనలోకి తీసుకుంటోంది . మీరు దానిని GSONకి మార్చినట్లయితే, చాలా మటుకు, మీరు కొంచెం ఎక్కువ సమయం "గెలవవచ్చు".

ఈ సమయంలో, నేను ప్రోగ్రామర్ మరియు సమయం గురించి ఒక జోక్ గుర్తుంచుకున్నాను: మీ కోడ్‌ను ఎలా వ్రాయాలి మరియు ఆప్టిమైజ్ చేయాలి అనే దాని గురించి చదివి ఆలోచించండి.

వ్యాఖ్యలు
  • జనాదరణ పొందినది
  • కొత్తది
  • పాతది
వ్యాఖ్యానించడానికి మీరు తప్పనిసరిగా సైన్ ఇన్ చేసి ఉండాలి
ఈ పేజీకి ఇంకా ఎలాంటి వ్యాఖ్యలు లేవు