CodeGym/Java Course/All lectures for TA purposes/தொடர்புடைய திட்டம்: SQL, JDBC மற்றும் Hibernate

தொடர்புடைய திட்டம்: SQL, JDBC மற்றும் Hibernate

கிடைக்கப்பெறுகிறது

இன்று நாம் நான்காவது JRU தொகுதியில் இறுதி திட்டத்தை செய்வோம். இது என்னவாகியிருக்கும்? MySQL, Hibernate, Redis, Docker: பல்வேறு தொழில்நுட்பங்களுடன் பணிபுரிய முயற்சிப்போம். இப்போது மேலும் பொருள்.

பணி: எங்களிடம் திட்டவட்டமான MySQL தரவுத்தளம் உள்ளது (நாடு-நகரம், நாடு வாரியாக). மேலும் நகரின் அடிக்கடி கோரிக்கை உள்ளது, இது மெதுவாக உள்ளது. நாங்கள் ஒரு தீர்வைக் கொண்டு வந்தோம் - அடிக்கடி கோரப்படும் எல்லா தரவையும் Redis க்கு நகர்த்துவதற்கு (முக்கிய மதிப்பு வகையின் நினைவக சேமிப்பகத்தில்).

மேலும் MySQL இல் சேமிக்கப்பட்டுள்ள எல்லா தரவுகளும் நமக்குத் தேவையில்லை, ஆனால் தேர்ந்தெடுக்கப்பட்ட புலங்களின் தொகுப்பு மட்டுமே. திட்டம் ஒரு பயிற்சி வடிவில் இருக்கும். அதாவது, இங்கே நாம் பிரச்சினையை எழுப்பி உடனடியாக அதைத் தீர்ப்போம்.

எனவே, நமக்கு என்ன மென்பொருள் தேவை என்று ஆரம்பிக்கலாம்:

  1. ஐடிஇஏ அல்டிமேட் (சாவி தீர்ந்துவிட்டது - ரோமன் இன் தி ஸ்லாக்கிற்கு எழுதுங்கள்)
  2. வொர்க்பெஞ்ச் (அல்லது MySQLக்கான வேறு ஏதேனும் கிளையன்ட்)
  3. டோக்கர்
  4. redis-insight - விருப்பமானது

எங்கள் செயல் திட்டம்:

  1. டோக்கரை அமைக்கவும் (இதை நான் டுடோரியலில் செய்ய மாட்டேன், ஏனென்றால் ஒவ்வொரு OS க்கும் அதன் சொந்த குணாதிசயங்கள் இருக்கும் மற்றும் "விண்டோஸில் டோக்கரை எவ்வாறு நிறுவுவது" போன்ற கேள்விகளுக்கு இணையத்தில் நிறைய பதில்கள் உள்ளன), எல்லாம் செயல்படுகிறதா என்று சரிபார்க்கவும்.
  2. MySQL சேவையகத்தை டாக்கர் கொள்கலனாக இயக்கவும்.
  3. டம்பை விரிவாக்கு .
  4. ஐடியாவில் ஒரு திட்டத்தை உருவாக்கவும், மேவன் சார்புகளைச் சேர்க்கவும்.
  5. அடுக்கு டொமைனை உருவாக்கவும்.
  6. MySQL இலிருந்து எல்லா தரவையும் பெற ஒரு முறையை எழுதவும்.
  7. தரவு மாற்றும் முறையை எழுதவும் (ரெடிஸில் அடிக்கடி கோரப்படும் தரவை மட்டுமே எழுதுவோம்).
  8. ரெடிஸ் சேவையகத்தை டாக்கர் கொள்கலனாக இயக்கவும்.
  9. ரெடிஸுக்கு தரவை எழுதவும்.
  10. விருப்பமானது: redis-insight ஐ நிறுவவும், Redis இல் சேமிக்கப்பட்ட தரவைப் பார்க்கவும்.
  11. Redis இலிருந்து தரவைப் பெறுவதற்கான முறையை எழுதவும்.
  12. MySQL இலிருந்து தரவைப் பெறுவதற்கான முறையை எழுதவும்.
  13. MySQL மற்றும் 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, மீண்டும் டம்பை வரிசைப்படுத்த வேண்டிய அவசியமில்லை - அது ஏற்கனவே தொகுதியில் வரிசைப்படுத்தப்பட்டுள்ளது.

ஐடியாவில் திட்டத்தை உருவாக்கவும், மேவன் சார்புகளைச் சேர்க்கவும்

ஐடியாவில் ஒரு திட்டத்தை எவ்வாறு உருவாக்குவது என்பது உங்களுக்கு ஏற்கனவே தெரியும் - இது இன்றைய திட்டத்தில் எளிதான புள்ளி.

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> 

முதல் மூன்று சார்புகள் உங்களுக்கு நீண்ட காலமாக நன்கு தெரிந்தவை.

lettuce-coreரெடிஸ் உடன் பணிபுரிய கிடைக்கக்கூடிய ஜாவா கிளையண்டுகளில் ஒன்றாகும்.

jackson-databind- ஆப்ஜெக்ட்மேப்பரைப் பயன்படுத்துவதற்கான சார்பு (ரெடிஸில் சேமிப்பிற்கான தரவை மாற்றுவதற்கு (வகை சரத்தின் முக்கிய மதிப்பு)).

ஆதாரங்கள் கோப்புறையில் (src/main/resources) கோரிக்கைகளை Hibernate செயல்படுத்தும் அளவுருக்களுடன் பார்க்க 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 இலிருந்து எல்லா தரவையும் பெறுவதற்கான முறையை எழுது" உருப்படிக்கு வரும்போது, ​​Hibernate என்ன வினவல்களை செயல்படுத்துகிறது என்பதைப் பார்ப்போம், அவற்றின் எண்ணைப் பார்த்து, இந்த உருப்படியை நினைவில் வைத்துக் கொள்ளுங்கள்.

நகர வகுப்பு குறியீடு:

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

இப்போது நாம் எங்கள் பயன்பாட்டை முதல் முறையாக பிழைத்திருத்தத்தில் இயக்கலாம் மற்றும் அது எவ்வாறு செயல்படுகிறது என்பதைப் பார்க்கலாம் (அல்லது வேலை செய்யாது - ஆம், இது அடிக்கடி நடக்கும்).

நகரங்கள் பெறுகின்றன. ஒவ்வொரு நகரமும் ஒரு நாட்டைப் பெறுகிறது, அது மற்றொரு நகரத்திற்கான தரவுத்தளத்திலிருந்து முன்னர் கழிக்கப்படவில்லை என்றால். தரவுத்தளத்திற்கு Hibernate எத்தனை வினவல்களை அனுப்பும் என்பதை தோராயமாக கணக்கிடுவோம்:

  • நகரங்களின் மொத்த எண்ணிக்கையைக் கண்டறிய 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.redis2 வகுப்புகளைச் சேர்க்கும் தொகுப்பை உருவாக்குவோம் : 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());
}

இந்த முறை சுய விளக்கமளிக்கும் என்று நான் நினைக்கிறேன்: நாங்கள் ஒரு CityCountry நிறுவனத்தை உருவாக்கி, நகரம் , நாடு , நாட்டு மொழி ஆகியவற்றிலிருந்து தரவை நிரப்புகிறோம் .

ரெடிஸ் சேவையகத்தை டாக்கர் கொள்கலனாக இயக்கவும்

இங்கே 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 

மேலும் இது போன்ற ஒன்றை நீங்கள் காண்பீர்கள்:

நீங்கள் சில கட்டளைகளைக் கண்டுபிடிக்க வேண்டும் என்றால், முனையத்தில் உள்ள உதவியைப் பார்க்கலாம் (டாக்கர் உதவி) அல்லது கூகிள் "எப்படி ..." (உதாரணமாக, இயங்கும் கொள்கலனை அகற்றுவது எப்படி டோக்கர்).

மேலும் பிரதான கட்டமைப்பாளரில் முள்ளங்கி கிளையண்டின் துவக்கத்தையும் நாங்கள் அழைத்தோம், ஆனால் அந்த முறையை செயல்படுத்தவில்லை. செயல்படுத்தலைச் சேர்க்கவும்:

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 சேர்க்கப்பட்டது, இதனால் வெளியீட்டு பதிவில் எல்லாம் சரியாக இருப்பதையும், முள்ளங்கி கிளையன்ட் வழியாக இணைப்பு பிழைகள் இல்லாமல் கடந்து சென்றதையும் காணலாம்.

ரெடிஸுக்கு தரவை எழுதவும்

முக்கிய முறைக்கு அழைப்பைச் சேர்க்கவும்

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-insight ஐ நிறுவவும், Redis இல் சேமிக்கப்பட்ட தரவைப் பார்க்கவும் (விரும்பினால்)

இணைப்பிலிருந்து redis-insight ஐப் பதிவிறக்கி நிறுவவும். தொடங்கிய பிறகு, அது உடனடியாக டாக்கர் கொள்கலனில் எங்கள் முள்ளங்கி நிகழ்வைக் காட்டுகிறது:

நீங்கள் உள்நுழைந்தால், அனைத்து விசைகளின் பட்டியலைக் காண்போம்:

மேலும் எந்த விசையில் எந்த தரவு சேமிக்கப்பட்டுள்ளது என்பதைப் பார்க்க நீங்கள் செல்லலாம்:

Redis இலிருந்து தரவைப் பெறுவதற்கான முறையை எழுதவும்

சோதனைக்கு, நாங்கள் பின்வரும் சோதனையைப் பயன்படுத்துகிறோம்: நாங்கள் 10 சிட்டி கன்ட்ரி பதிவுகளைப் பெறுகிறோம். ஒவ்வொன்றும் தனித்தனி கோரிக்கையுடன், ஆனால் ஒரு இணைப்பில்.

முள்ளங்கியில் இருந்து தரவை எங்கள் முள்ளங்கி கிளையன்ட் மூலம் பெறலாம். இதைச் செய்ய, ஐடிகளின் பட்டியலைப் பெறுவதற்கான முறையை எழுதுவோம்.

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

செயல்படுத்தல், உள்ளுணர்வு என்று நான் நினைக்கிறேன்: நாங்கள் ஒரு ஒத்திசைவான இணைப்பைத் திறக்கிறோம், மேலும் ஒவ்வொரு ஐடிக்கும் ஒரு 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 க்கு மாற்றினால், நீங்கள் இன்னும் சிறிது நேரம் "வெற்றி" பெறலாம்.

இந்த நேரத்தில், ஒரு புரோகிராமர் மற்றும் நேரத்தைப் பற்றிய ஒரு நகைச்சுவை எனக்கு நினைவிருக்கிறது: உங்கள் குறியீட்டை எவ்வாறு எழுதுவது மற்றும் மேம்படுத்துவது என்பதைப் படித்து சிந்தியுங்கள்.

கருத்துக்கள்
  • பிரபலமானவை
  • புதியவை
  • பழையவை
ஒரு கருத்தைத் தெரிவிக்க நீங்கள் உள்நுழைந்திருக்க வேண்டும்
இந்தப் பக்கத்தில் இதுவரை எந்தக் கருத்தும் வழங்கப்படவில்லை