Quickest Commission & Immediate Withdrawal Gambling enterprises in the Canada 2025

As an alternative, you might deposit €1,one hundred thousand and/or CAD equal to get to the Gold position during the just after. Addititionally there is a respect program program which have twenty-five levels, away from Forest Sage to King away from Thrones. To possess getting together with for each the brand new height, professionals score a supplementary prize, such 3 hundred Totally free Spins that have a low 5x bet for the best. 继续阅读Quickest Commission & Immediate Withdrawal Gambling enterprises in the Canada 2025

Spinrise Gambling establishment Advice Everything we discover! Up-to-day

I fulfilled kind of laws otherwise issues that someone don't appreciate, but all in all, we think about the T&Cs taking generally practical. The new Spinrise VIP Plan gets me personally a far more private and you may you might enjoyable time as i glance at the Bronze, Silver, Gold, Platinum, and Rubin subscription. Each step opens up best perks – quicker cash outs, bigger limitations, unique bonuses and you can chances to check in contes͏ts. 继续阅读Spinrise Gambling establishment Advice Everything we discover! Up-to-day

Spin rise SpinRise Login für jedes Zocker within Deutschland angeschlossen

Schön im griff haben unsereins jedoch hervorheben, wirklich so inside sämtlichen Einzahlungen keine weiteren Transaktionsgebühren pro Zocker fällig werden. Daneben dem Willkommenspaket ferner diesem High Roller Bonus auf den füßen stehen dir inoffizieller mitarbeiter Spinrise Casino jedoch einige viel mehr Aktionen zur Order, unser wir dir nicht nicht zugestehen intendieren. 继续阅读Spin rise SpinRise Login für jedes Zocker within Deutschland angeschlossen

Greatest On-line casino Incentive Now offers 2025 Allege Your Free Incentives

Alternatively, the newest local casino offers you a little bit of extra fund in order to have fun with and you may earn real cash instead getting the fund at risk. We offer a call at-depth self-help guide to no deposit incentives here, and you may a whole self-help guide to our no-deposit codes which have head entry to an entertaining databases unit right here. 继续阅读Greatest On-line casino Incentive Now offers 2025 Allege Your Free Incentives

নতুন ওয়েব ভিত্তিক ক্যাসিনোগুলি দেখুন আগস্ট ২০২৫ এর সর্বশেষ সাইটগুলি

অনেক অংশগ্রহণকার ek333bangladesh.com ীদের সুপরিচিত পেমেন্ট পদ্ধতি আছে, তা সে বিটকয়েন, চার্জ, অথবা অন্য কেউ হোক। যদি আপনি নতুন ক্যাসিনো হন তবে সাধারণত নির্দিষ্ট ফি বিকল্পগুলি অফার করে, মনে রাখবেন যে আপনার পছন্দের কৌশলের উপর নির্ভর করে নির্দিষ্ট বোনাসগুলি পাওয়া যাবে না। আপনার পছন্দের শতাংশ কৌশলটি যোগ্য কিনা তা নিশ্চিত করতে অফারের ছোট মুদ্রণটি পড়ুন। শুভকামনা নতুন অনলাইন ক্যাসিনোগুলি তাদের ওয়েবসাইটগুলির জন্য চার্জ জুয়া সংস্থান সরবরাহ করে।

SlotsandCasino – নতুন নিবন্ধিত পেশাদারদের জন্য সেরা স্বাগত প্রণোদনা পরিকল্পনা

  • BetWhale-এর লাইভ ব্রোকার বিকল্প রয়েছে যেমন ব্ল্যাক-জ্যাক, রুলেট এবং ব্যাকার্যাট, যা নিশ্চিত করে যে অংশগ্রহণকারীরা রিয়েল-টাইম ফর্ম্যাটে বেশ কয়েকটি জনপ্রিয় অনলাইন ক্যাসিনো গেমে অ্যাক্সেস পান।
  • নির্ভরশীল প্রোগ্রামের পরিবর্তে, নতুন ওয়েব ভিত্তিক ক্যাসিনোগুলি সাধারণত কাউন্টি-অফ-দ্য-আর্টওয়ার্ক সফ্টওয়্যার সহ আসে, যা সহজ এবং নিমজ্জিত গেম খেলা নিশ্চিত করে।
  • iGaming বিশ্বে ব্র্যান্ড প্রোফাইল অত্যন্ত গুরুত্বপূর্ণ, এবং নতুন ক্যাসিনো কোম্পানিগুলির কাছে নির্ভরযোগ্য এবং নির্ভরযোগ্য অপারেটরদের সাথে নিজেদের পরিচয় করিয়ে দেওয়ার সময় নেই।
  • লিন্টন মিনিমাল থেকে কাজ করা এবং পরিচালনা করা, ইটারি স্থানীয় ক্যাসিনো 2016 সালে তার প্রতিষ্ঠান হিসেবে অনলাইন বেটিং নেটওয়ার্কে ঘুরে বেড়াতে পারে।

MYB জুয়া প্রতিষ্ঠানের জনপ্রিয় বৈশিষ্ট্যগুলির মধ্যে রয়েছে নিরাপদ শতাংশ বিকল্প, যার মধ্যে রয়েছে ভিসা, চার্জ কার্ড এবং দশটি সাধারণ ক্রিপ্টোকারেন্সি। এই জুয়া প্রতিষ্ঠানটি জাল কার্যকলাপ প্রতিরোধের জন্য সর্বোচ্চ স্তরের সুপারিশ সুরক্ষার নিশ্চয়তা দেয়, অনলাইনে নিরাপদ জুয়া নিশ্চিত করার জন্য 256-অংশ SSL সুরক্ষা ব্যবহার করে। আপনি কি কখনও ভেবে দেখেছেন যে অনলাইন ক্যাসিনোর ক্ষেত্র সম্পর্কে কী বলা উচিত? নতুন গেম, সুবিধা এবং সৃজনশীল বৈশিষ্ট্যগুলি বোঝার নতুন অভিযান সত্যিই উৎসাহব্যঞ্জক! কিন্তু খেলার চাহিদা পূরণের জন্য আপনি কীভাবে কোন পূর্ববর্তী ক্রমবর্ধমান দেশটি ব্রাউজ করবেন? চিন্তা করবেন না, এই বইটিতে, আমরা আপনাকে 2025 সালের সর্বশেষ অনলাইন ক্যাসিনোগুলির সাথে পরিচয় করিয়ে দেব এবং কীভাবে সেরা জুয়া বিকল্পটি পূরণ করবেন তার নির্দেশিকা প্রদর্শন করব।

মার্কিন যুক্তরাষ্ট্রে আরও ভালো ওয়েব ভিত্তিক ক্যাসিনো: বিচারক কী?

আমি পেশাদার অংশগ্রহণ উন্নত করার জন্য গেমিফিকেশনের দিকগুলিও দেখি। বিনোদনমূলক চাহিদা, লিডারবোর্ড এবং অন্যান্য গেমিফিকেশন অনলাইন ক্যাসিনো বেটিং অভিজ্ঞতাকে আরও উপভোগ্য এবং সন্তোষজনক করে তুলেছে। ধারাবাহিক র‍্যাঙ্কিং বিভাগগুলির মাধ্যমে, আমরা অনলাইনে সেরা নতুন ক্যাসিনোগুলি সনাক্ত করতে পারি যা উচ্চমানের অনলাইন গেম, দুর্দান্ত সুবিধা এবং খেলোয়াড়দের জন্য একটি নিরাপদ বাস্তুতন্ত্র প্রদান করে। রিয়েল-টাইম ব্রোকার গেম হল নতুন অনলাইন ক্যাসিনোর আরেকটি লক্ষ্য। এই ধরণের গেমগুলি একটি খাঁটি জুয়া খেলার পরিবেশ প্রদান করে, যা মানুষকে রিয়েল-টাইমে লাইভ খেলোয়াড় এবং অন্যান্য পেশাদারদের সাথে যোগাযোগ করতে সক্ষম করে। এই বৈশিষ্ট্যটি সম্পূর্ণ অনলাইন জুয়ার অনুভূতি বাড়ায়, তাই এটি আরও বিনোদনমূলক এবং উপভোগ্য।
online casino real money no deposit
সহজ নকশা জুয়ার বাস্তুতন্ত্রকে নান্দনিকভাবে আকর্ষণীয় করে তুলতে সাহায্য করে। নতুন অনলাইন ক্যাসিনোগুলির বিশ্লেষণে শব্দের ভাঙ্গন, সিস্টেম সুরক্ষা, অনলাইন গেমের ন্যায্যতা এবং আরও অনেক কিছু অন্তর্ভুক্ত। যখন Gambling establishment.org একটি নতুন অনলাইন ক্যাসিনোকে নিরাপদ হিসেবে স্ট্যাম্প করে, তখন এর অর্থ হল প্রোগ্রামটি একটি উচ্চমানের পাব সরিয়ে দেয় — যা খেলোয়াড়দের যথেষ্ট সময়-পরিচয় রক্ষা করে। নতুন ক্যাসিনো এবং প্রচারগুলিতে অংশগ্রহণের জন্য আগ্রহী খেলোয়াড়দের জন্য, সর্বশেষ অনলাইন ক্যাসিনোগুলি সম্পর্কে জানা গুরুত্বপূর্ণ।

ইন্টারনেটে নতুন ক্যাসিনো 2024 মার্কিন যুক্তরাষ্ট্র

৬০০ টিরও বেশি ভিডিও গেম, ৫০০ টিরও বেশি পোর্ট, প্রায় ১০০ টেবিল গেম এবং সেই সমস্ত রিয়েল-টাইম এজেন্ট গেম সহ, ডোমিন্যান্স লঞ্চার ক্যাসিনো সকলের জন্য কিছু না কিছু অফার করে। অ্যালাইভ ডিলার অনলাইন গেম হল ব্যালি-প্রাইভেট শিরোনাম এবং ব্ল্যাক-জ্যাক এবং রুলেট সহ ক্লাসিক। ২০২৫ সালে সেরা অনলাইন ক্যাসিনো অভিজ্ঞতা পেতে, আরও উল্লেখ করা শীর্ষ ১০টি সেরা অনলাইন ক্যাসিনো দেখুন। ওয়েবসাইটগুলি একটি ভাল জুয়ার অভিজ্ঞতা নিশ্চিত করার জন্য অসংখ্য গেম এবং প্রচুর বোনাস অফার করে। অনলাইন ক্যাসিনোগুলি দায়িত্বশীল গেমিং পণ্য প্রদান করে যেমন মোড ব্যক্তিগত জুয়ার সীমাবদ্ধতা এবং তাদের স্থায়ী জুয়ার অভ্যাসের জন্য টিপস অ্যাক্সেস। TLS এবং হাইব্রিড RSA সহ নিরাপত্তা প্রযুক্তি নিরাপদ অনলাইন ক্যাসিনো দ্বারা ব্যবহার করা যেতে পারে।

নিবন্ধিত প্ল্যাটফর্মগুলি কীভাবে নিরাপদ খেলা নিশ্চিত করে

আমাদের সেরা নির্বাচনগুলি তাৎক্ষণিক অবস্থান এবং দ্রুত নগদ অর্থ প্রদান করে যার মধ্যে নমনীয় সীমাবদ্ধতা (কম সর্বনিম্ন, উচ্চ সর্বোচ্চ) এবং বিভিন্ন ধরণের শতাংশ পদ্ধতি রয়েছে — ক্রিপ্টো সমন্বিত। আপনি যদি সত্যিকার অর্থে আয়ের অনলাইন ক্যাসিনো গেম খুঁজছেন, অনলাইন ক্যাসিনো, অথবা আপনার কাউন্টিতে কোন জুয়া খেলার আদালত রয়েছে তা পরীক্ষা করছেন — এই পৃষ্ঠাটি সেরা বিকল্প। আমি 2025 সালে খেলোয়াড়দের জন্য ইট-পাথরের দোকান থেকে শুরু করে সেরা জুয়া খেলার সাইট পর্যন্ত সবকিছুই সুরক্ষিত রাখি।

  • আত্মবিশ্বাসী হোন, আমি যে সমস্ত ওয়েবসাইটগুলিকে সুপারিশ করছি সেগুলি সম্পূর্ণরূপে অনুমোদিত এবং নিরাপদ, যত্ন নেওয়ার পরিবর্তে অনুভূতি দেখার উপর মনোনিবেশ করা।
  • এই পৃষ্ঠায়, আমরা বড় ব্র্যান্ড - নতুন জুয়া সংস্থাগুলি নিয়ে আলোচনা করেছি যা আপনাকে অবশ্যই নতুন জুয়ার অভিজ্ঞতা নিশ্চিত করবে।
  • এই ধারণার কারণে, স্লটস্পট অনলাইন ক্যাসিনো সুরক্ষিত করার চেষ্টা করা যে কোনও ব্যক্তির জন্য একটি স্পিন-টু সাইট হয়ে ওঠে, যার উপর নির্ভর করে কেবল বিশ্বাসযোগ্যতাই নয়।
  • এই ধরণের পদ্ধতি অনুসরণ করে, আপনি নতুন অনলাইন ক্যাসিনোগুলির কারণে বিবেচনা করা বড় প্রণোদনা এবং অফারগুলি থেকে উপকৃত হতে পারেন এবং আপনার বাজির অনুভূতিটি অনুকূল করতে পারেন।

নতুন অনলাইন ক্যাসিনো ইউএস-এর জন্য নিয়ন্ত্রণে থাকা বাজির টিপস একটি গুরুত্বপূর্ণ বিষয়, যা ব্যবহারকারীর আগ্রহের সাথে অগ্রগতির ভারসাম্য বজায় রাখার প্রতি তাদের নিষ্ঠা প্রদর্শন করে। এই অগ্রগতিগুলি কেবল খেলোয়াড়দের অনুভূতি উন্নত করে না এবং অনলাইন ক্যাসিনোগুলিকে বৈচিত্র্যময়, প্রযুক্তি-স্মার্ট দর্শক আকর্ষণ করার জন্য পরিবেশন করে। ব্ল্যাক লোটাসের মতো প্ল্যাটফর্মগুলি মসৃণ, আকর্ষণীয় এবং প্রগতিশীল গেমিং পরিবেশ প্রদানের জন্য এই ধরণের প্রযুক্তি ব্যবহার করে। বেশিরভাগ প্রক্রিয়ার জন্য কমপক্ষে $20 (বেশিরভাগ ক্রিপ্টো বিকল্পের জন্য $10) থেকে শুরু হয়, যার তাৎক্ষণিক চার্জ ছাড়াই পরিচালনা করা যায়। অপারেটিং প্ল্যাটফর্মটি USD, CAD, EUR, INR, NOK এবং DKK সহ বিভিন্ন মুদ্রাকে সমর্থন করে, যা সীমাবদ্ধতার বাইরে পেশাদারদের থাকার স্বাধীনতা নিশ্চিত করে। সমস্ত লেনদেন 128-অংশ SSL এনক্রিপশন দ্বারা আচ্ছাদিত, সুরক্ষার জন্য নেটওয়ার্ক মান মেনে চলে।
online casino no deposit bonus
এছাড়াও, নতুন অনলাইন ক্যাসিনোগুলি ব্যবহারকারীদের পছন্দের উপর ভিত্তি করে তৈরি করা হয়। তারা গেম বুক করে এবং আরও পরিপক্ক ক্যাসিনো সংস্থাগুলিকে আপনার সমস্ত কিছু ব্যবহার করতে দেয়, যাতে আপনার খেলার অনুভূতি তাজা এবং মজাদার হয়। একটি ভিন্ন ক্যাসিনো ওয়েবসাইট বেছে নেওয়ার ফলে গেমটিতে অ্যাক্সেস এবং আপনার উপায় এবং পছন্দ অনুসারে একটি প্ল্যাটফর্ম থাকে। সামগ্রিকভাবে, অনলাইন ক্যাসিনোগুলি ঐতিহ্যবাহী ক্যাসিনো ছাড়াই একটি নতুন এবং মজাদার ক্যাসিনো অভিজ্ঞতা প্রদান করে। নতুন গেম, প্রযুক্তি, বৈশিষ্ট্য যোগ করার মাধ্যমে, এই ক্যাসিনোগুলি ব্যবহারকারীদের আরও মজাদার এবং সন্তোষজনক অনুভূতি প্রদান করতে সক্ষম। এছাড়াও, অনলাইন ক্যাসিনোগুলি নতুন পেশাদারদের আকর্ষণ করার জন্য আরও উদার গ্রহণযোগ্যতা প্রণোদনা এবং প্রচার প্রদান করে।

Porsche: Precision Crafted Performance

Porsche is known for its perfect balance between luxury and sportiness. The brand focuses on precision engineering and driving control.

Porsche vehicles are praised for exceptional handling and responsiveness. They perform equally well on everyday roads and race tracks.

This combination makes Porsche a symbol of refined performance and technical excellence.

Pin Up Casino – Официальный сайт казино Пин Ап: способы входа через альтернативные зеркала (2025)

▶️ ИГРАТЬ

В сфере онлайн-гемблинга, где постоянно возникают новые площадки, подбор действительно защищенного и стабильного клуба может представлять сложность. Pin Up Casino — один из таких удачных вариантов. Это обладающий лицензией оператор, предоставляющий своим пользователям широкую коллекцию азартных продуктов: автоматы, карточные игры, рулетку и многое другое.

Pin Up Casino — международная игровая платформа, созданная в 2016 году. За время существования она смогла заработать доверие игроков по всему миру за счет продвинутых систем защиты, гарантирующих сохранность личных данных.

Официальный ресурс казино дает гостям полный набор функций: авторизацию в учетной записи, получение поощрений и участие в турнирах. В распоряжении пользователей — свыше 3000 развлечений, включая слоты, игры с живыми дилерами и рулетку. Дополнительные эмоции обеспечивают акционные предложения и состязания.

Pin Up Casino также славится высоким уровнем безопасности. Площадка применяет современные алгоритмы шифрования для предотвращения недобросовестных действий. Наличие международной лицензии доказывает её надежность и устойчивость.

Если вы в поисках проверенного онлайн-казино для игры на реальные деньги с возможностью вывода выигрышей — Pin Up Casino окажется прекрасным выбором. Официальный сайт обеспечивает все условия для захватывающего и удобного отдыха.

Внимание! Для перехода на зеркало Pin Up Casino применяйте исключительно проверенные источники, чтобы не оказаться жертвой злоумышленников. Не переходите по ссылкам на сомнительные сайты.

Pin Up Casino – ваш надежный партнер на пути к успеху!

Pin Up Casino – Официальный портал Пин Ап.

Pin Up Casino – это известная игровая площадка, предлагающая пользователям многообразные развлекательные и финансовые опции. Официальный сайт служит центральным узлом для безопасного и комфортного досуга.

На платформе доступен широкий выбор игр: автоматы, настольные игры, рулетка, бинго и прочие. Все они разработаны с использованием современных технологий, что обеспечивает качественную графику и стабильную работу.

Помимо этого, казино поощряет активность пользователей бонусами и наградами за привлечение друзей. Это повышает шансы на победу и делает игровой процесс ещё увлекательнее.

Таким образом, официальный сайт Pin Up Casino предоставляет всё нужное для качественного отдыха и игры.

Доступ через альтернативные адреса (2025)

В условиях серьезной конкуренции в области азартных развлечений Pin Up Casino удалось выделиться и занять лидирующие места. Этому поспособствовало не только разнообразие контента, но и разные способы доступа к платформе.

Один из таких способов – вход через зеркало сайта. Зеркало является альтернативным адресом, дающим возможность получить доступ к казино при блокировке или неработоспособности главного сайта. Это особенно значимо для игроков, которые сталкиваются с ограничениями.

Вход через зеркало Pin Up Casino – это легкий и оперативный способ подключиться к играм. Достаточно ввести адрес зеркала в браузере и авторизоваться привычным методом – через социальные сети или логин с паролем.

Через зеркало предоставляется полный доступ ко всем играм казино: слотам, рулетке, бинго и другим. Это дает возможность играть когда угодно и где угодно при наличии интернета.

В целом, вход через зеркало Pin Up Casino – это удобный и надежный способ обхода блокировок для продолжения игры.

Сильные стороны и функционал Pin Up Casino.

Pin Up Casino – это известная игровая площадка, предлагающая пользователям обширный каталог автоматов, азартных игр и развлекательных программ. Вот главные достоинства и функции, которые позволяют ему быть одним из лучших в России.

Уникальные возможности.

Большой выбор игровых автоматов и азартных игр Высокие ставки и выплаты Мобильная версия сайта для пользователей смартфонов и планшетов Многоязычный интерфейс Квалифицированная служба поддержки.

Преимущества.

Pin Up Casino – это место, где вы можете насладиться игрой и получать выигрыши. Сайт предлагает широкий набор функций и преимуществ, которые делают его одним из лучших онлайн-казино в России.

Как начать играть в Pin Up Casino.

Чтобы начать играть в Pin Up Casino, необходимо зарегистрироваться на официальном сайте pinup.casino. Процесс создания учётной записи отнимет всего несколько минут.

Нужно выбрать тип аккаунта: игрок или дилер. Для игры выбирайте опцию «игрок».

Шаг 1: регистрация.

Для регистрации потребуется указать личные данные: имя, фамилию, дату рождения, e-mail и номер мобильного телефона. Также необходимо придумать пароль.

После заполнения формы на электронную почту придет письмо с подтверждением. Нужно перейти по ссылке для активации аккаунта.

Шаг 2: пополнение счета.

После регистрации требуется внести депозит. Доступные методы пополнения: кредитная карта, электронный кошелек, банковский перевод.

Выберите подходящий способ, укажите сумму пополнения и предпочтительную валюту.

После пополнения баланса откроется доступ ко всем игровым автоматам и столам.

Начните играть, выбрав понравившийся слот или игру. Доступны слоты, рулетка, бинго и другие развлечения.

Также игрокам доступны различные бонусы и акции, которые помогут начать игру и повысить шансы на выигрыш.

Наслаждайтесь игрой в Pin Up Casino!

Elasticsearch Query DsL查询


Query DsL查询

一 Elasticsearch简介

Elasticsearch 是一个开源的搜索引擎,Elasticsearch 使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单, 通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。

  • 一个分布式的实时文档存储,每个字段 可以被索引与搜索
  • 一个分布式实时分析搜索引擎
  • 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据

 

二 安装并运行

已经在其他文档中详细介绍,此次仅做简单步骤介绍

# 安装:
$   wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.13.3-linux-x86_64.tar.gz
$   tar -xzf elasticsearch-7.13.3-linux-x86_64.tar.gz 
$   cd elasticsearch-7.13.3/
# 运行
sh bin/elasticsearch

# 访问
$ curl http://192.168.3.14:9200/ 
{
  "name" : "87DNZWU",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "e3A3l85MSZuZlRhxj6IB2w",
  "version" : {
    "number" : "6.7.0",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "8453f77",
    "build_date" : "2019-03-21T15:32:29.844721Z",
    "build_snapshot" : false,
    "lucene_version" : "7.7.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

 

三 Query DSL 基本结构

查询表达式(Query DSL)是一种非常灵活又富有表现力的查询语言, Elasticsearch 使用它可以以简单的 JSON 接口来展现 Lucene 功能的绝大部分

// 查询
GET /_search  // 查找整个ES中所有索引的内容
{
  "query": {}, //具体的查询语句对象
  "from": 0,   //从第几条数据开始返回
  "size": 100, //返回的条数 默认ES最多返回10000条
  "highlight": { //高亮
    "pre_tags": {}, //高亮内容的前面标签 一般都是html比如<b> <p>这种
    "post_tags": {},//高亮内容的后面标签 一般都是html比如</b> </p>这种
    "fields": { //需要高亮的字段
    }
  },
  "sort": [{ //排序
    "FIELD": { //排序的字段(需要填上具体的字段名)
      "order": "desc"
    }
  }],
  "_source": "{field}" //指定返回的字段
}

// 结果
{
    "took": 350,  // 整个搜索请求消耗了多少毫秒
    "timed_out": false, // 表示本次查询是否超时,如果为true也会返回结果,只是数据可能不完整
    "_shards": { //  显示查询中参与的分片信息,成功多少分片失败多少分片等
        "total": 5, 
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 5, // 匹配到的文档总数
        "max_score": 1, // 为文档中所有_score的最大值
        "hits": [
            {
                "_index": "mysql-shop_trades-order_item_label_binds",
                "_type": "doc",
                "_id": "591935",
                "_score": 1,
                "_source": {
                    "id": 591935,
                    "updated_at": "2021-05-20T06:26:09.000Z",
                    "@version": "1",
                    "bind_item_label_id": 729,
                    "label_type": "brand",
                    "created_at": "2021-05-20T06:26:09.000Z",
                    "@timestamp": "2021-07-07T07:31:36.262Z",
                    "is_deleted": 0,
                    "table_name": "order_item_label_binds",
                    "bar_code": "6907925004486"
                }
            }
        ]
    }
}

 

四 指定索引搜索

 

上述查询会搜索ES中的所有索引,但通常只需要去固定一个或几个索引中搜索,搜索全部无疑会造成资源的浪费,在ES中可以通过以下几种方法来指定索引

  1. 指定一个固定的索引,ops-coffee-nginx-2019.05.15为索引名字
GET /mysql-shop_trades-order_statics/_search

以上表示在mysql-shop_trades-order_statics索引下查找数据

  1. 指定多个固定索引,多个索引名字用逗号分割
GET /mysql-shop_trades-order_statics,mysql-shop_trades-order_item_labels/_search

  1. 用*号匹配,在匹配到的所有索引下查找数据
GET /mysql-shop_trades-*/_search

这里也可以用逗号分割多个匹配索引

五 DSL查询

1、筛选字段

// 筛选_source的数据,单个字段
GET /_search
{
  "_source": "bar_code",  
  "query": {}
}

// 筛选_source的数据,多个字段
{
  "_source": {
    "includes": ["store_id", "sku_id"]
  },
  "query": {}
}

// 对字段进行转换
{
  "docvalue_fields": [
    {
      "field": "updated_at",
      "format": "yyyy-MM-dd HH:mm:ss"
    },
    {
      "field": "num",
      "format": "long" // 没有作用,懵逼...
    }
  ], 
  "query": {}
}

 

2、多条件查询 (where)

  1. constant_score:装另一个查询的查询,固定分数查询,支持filter查询,不支持match查询:
    {
        "constant_score": {
            "filter": {
                "match": {
                    "name": "小米"
                }
            },
            "boost": 10
        }
    }
    

     

  2. bool:主要与其他关键字组合使用,多条件的查询必须要用bool包在外层,然后再根据具体的业务来拼接。

{
  "query": {
    "bool": {
      "should": [{}], //满足其中一个对象查询条件就行 像sql里的or
      "must": [{}],   //必须满足所有对象的查询条件 就像sql里的and
      "must_not": [{}] //必须不满足所有对象的查询条件 就像sql里的and !=
    }
  }
}

 

  1. must: 类似于SQL中的AND,必须包含
  2. must_not: 类似于SQL中的NOT,必须不包含
  3. should: 满足这些条件中的任何条件都会增加评分_score,不满足也不影响,should只会影响查询结果的_score值,并不会影响结果的内容
  4. filter: 与must相似,但不会对结果进行相关性评分_score,大多数情况下我们对于日志的需求都无相关性的要求,所以建议查询的过程中多用filter

3、group by:

ES本身没有group关键词搜索,但支持聚合查询,,需要使用关键字aggs

// 单个字段 group by
{
  "query":{},//这里省略你的查询条件
  "aggs": {
    "age_group": {//这个是指你要返回字段名
      "terms": { //这里还可以用其它关键词 这里terms才能实现group by效果
        "field": "age",//groupby的字段
        "size":1 //返回的条数 相当于group by limit
      }
    }
  }
}

// 多字段group by (如 group by sku_id,store_id)
// 方法一:script
{
  "query":{},
  "aggs": {
    "age_group": {
      "terms": {
        "script":{
          "source": """ 's' + doc['store_id'] + '_s' + doc['sku_id'] """,
          "lang": "painless"
        },
        "size": 10
      }
    }
  }
}

// 方法二:copy to
1. 设置mapping中的多个字段,copy_to 为同一个字段(skuId_storeId)
2. 搜索新字段
{
  "query":{},
  "aggs": {
    "list": {
      "terms": { 
        "field": "skuId_storeId
        "size":1
      }
    }
  }
}

// 方法三:multi_terms (使用高版本,目前6.7不支持)
{
  "aggs": {
    "genres_and_products": {
      "multi_terms": {
        "terms": [{
          "field": "genre" 
        }, {
          "field": "product"
        }]
      }
    }
  }
}

4、order by

order by:注意日期格式和数值格式才支持排序;文本不支持,如果要排序, 需把字段设置为not analysis

// 单排序
{
    "query": {
        "sort": {
            "id": "desc"
        }
    }
}

// avg按照平均值排序
{
    "query": {
        "sort": [
            {
                "id": "desc"
            },
            {
                "price": {
                    "order": "asc",
                    "mode": "avg"
                }
            }
        ]
    }
}

5、count(distinct)

{
  "query":{},
  "aggs": {
    "total_sku_id": {
      "cardinality":{ "field": "sku_id"}
    },
     "total_entity_store_id": { // 非数字类型,无法使用field排序,可以对field增加fieldData = true,或者对field.keyword排序,建议使用后者,高效内存消耗低
      "cardinality":{ "field": "entity_store_id.keyword"}
    }
  }
}

 

6、SUM

{
  "query":{},
  "aggs": {
    "total_pay_num": {
      "sum": {"field": "num"}
    },
     "total_cost_fee": {
      "sum": {"field": "cost_fee"}
    }
  }
}

 

7、distinct :

select distinct(id) from table

{
  "query":{},
  "collapse": {
      "field": "id" //你需要distinct的字段
   }, 
}

 

8、limit

1. 分页:
    1. form:从第几个开始查询,最开始是0
    2. size:即limit
    3. 使用size,size最大可获取数量是xx个
2. 获取所有数据的三种方式
    1. scroll深度滚动需要根据scroll_id和循环取,取完后,需删除scroll,减小内存开销(深度滚动高效,用于处理大量数据,不适合实时获取)
    2.调整索引index.max_result_window的大小,默认10000 (大小与堆内存成正比,这个限制内存)
    3.search_after:请求需增加前一个结果的排序,(实时游标,可根据索引更新和删除而改变 )
    4。 如果是group by查询获取所有数据, 获取需要使用到cardinality查询预估总数,再使用partition、num_partitions分区依次获取数据

9、搜索关键字

  1. match:自定字段,根据字段关键字进行搜索,会分割关键词,匹配到含有一个多多个词的匹配
  2. query_string:全文搜索
  3. match_phrase:不分割关键词 {"match_phrase": {"name":"婴幼儿奶粉"}}
  4. term: 类似SQL where field = x,主要用于数字匹配;如果要匹配文本,会自动分词,不能精准查询,需把字段设置成not analysis
    {
      "query": {
        "term": {"bind_item_label_id": 729}
      }
    }
    

     

  5. terms: 类似SQL where field in (x,x),主要用于数字匹配,
    {
      "query": {
        "terms": {"bind_item_label_id": [703,729]}
      }
    }
    

     

  6. range:: 查询价格在1000-2000的商品
{
    "query": {
        "range": {
            "price": {
                "gte": 1000,
                "lte": 2000
            }
        }
    }
}

 

  1. filter:判断文档是否满足条件
{
    "query": {
        "bool": {
            "filter": {
                "term": {
                    "price": 1999
                }
            }
        }
    }
}

Elasticsearch:Aggregation

  1. metric:度量聚合,主要针对number类型的数据,需要es做较多的计算工作(类似SQL的SUM、MAX、AVG、MIN、Cardinality、stats<属于多值分析>等)
  2. bucket:桶聚合,划分不同步的桶,将数据分配到不同的桶,(类似SQL中的group by)
  3. Pipeline Aggregation:管道分析类型,对其他聚合结果进行二次聚合
  4. Matrix Aggregation:矩阵分析类型,支持对多个字段的操作并提供一个结果矩阵

term aggregation

  • size 可以通过size返回top size的文档,该术语聚合针对顶层术语(不包含嵌套词根),其搜索过程是将请求向所有分点发送请求,每个分片节点返回size条数据,然后聚合所有分片的结果(会对各分片返回的同样词根的数数值进行相加),最终从中挑选size条记录返回给客户端。从这个过程也可以看出,其结果并不是准确的,而是一个近似值。
  • Shard Size 为了提高该聚合的精确度,可以通过shard_size参数设置协调节点向各个分片请求的词根个数,然后在协调节点进行聚合,最后只返回size个词根给到客户端,shard_size >= size,如果shard_size设置小于size,ES会自动将其设置为size,默认情况下shard_size建议设置为(1.5 * size + 10)。
// 单个字段 group by
{
  "query":{},//这里省略你的查询条件
  "aggs": {
    "age_group": {//这个是指你要返回字段名
      "terms": { //这里还可以用其它关键词 这里terms才能实现group by效果
        "field": "age",//groupby的字段
        "size":1 //返回的条数 相当于group by limit
      }
    }
  }
}

// 返回结果格式
{
  ...
  "aggregations" : {
      "list" : {
        "doc_count_error_upper_bound" : 0, // 该值表示未进入最终术语列表的术语的最大潜在文档计数
        "sum_other_doc_count" : 90 // 该值表示未进入最终术语列表的术语的最大潜在文档计数
        "buckets" : [ // 返回doc_count排名最前的10个,受size参数的影响
          {
            "key" : "1",
            "doc_count" : 24,
            "total_refund_fee" : {
              "value" : 0.0
            },
            "total_cost_fee" : {
              "value" : -14976.0
            },
          }
        ]
      }
    }
    }
}

 

 

match_phrase :查询分析文本,创建词组查询

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query-phrase.html#query-dsl-match-query-phrase

 

 

举例子

GET mysql-shop_trades-order_item_label_binds/_search/?scroll=1m
{
  "docvalue_fields": [
    {
      "field": "updated_at",
      "format": "yyyy-MM-dd HH:mm:ss"
    }
  ], 
  "size": 1000,
  "sort": {"id":"desc"},
  "query": {
    "bool": {
      "must": [
        {"match": {"is_deleted": 0}},
        {"match": {"label_type": "brand"}},
        {
          "constant_score": {
            "filter": {
              "terms": {
                "bind_item_label_id": [703, 2, 729]
                }
              }
            }
        }
      ]
    }
    }
}




GET mysql-shop_trades-order_item_label_binds/_search/?scroll=1m
{
  "_source": "bar_code", 
  "query": {
    "bool": {
      "filter": [
        {"match": {"is_deleted": 0}},
        {"match_phrase": {"label_type": "brand"}},
        {"terms": {"bind_item_label_id": [703, 2, 729]}}
      ]
    }
  },
  "aggs": {
    "bar_code_group": {
      "terms": {
        "field": "bar_code.keyword",
        "size": 10 
      }
    }
  }
}


GET mysql-shop_trades-order_item_label_binds,mysql-shop_trades-order_statics/_search
{
  "query": {
    "bool": {
      "filter": [
        {"match_phrase": {"sys_name": "yiqigou"}},
        {"range": {"num": {"lte": 2000}}},
        {"range": {"return_num": {"gte": -1000}}},
        {"range": {"total_price": {"lte": 1000000}}},
          {"match": {"id": 60}},
        {"term": {"order_type": 0}},
        {"term": {"item_type": 0}},
        {"range": {"date": {
          "gte": "2020-01-21 00:00:00",
          "lte": "2021-07-22 00:00:00",
          "format": "yyyy-MM-dd HH:mm:ss",
          "time_zone": "+08:00"
        }}}
      ],
      "must_not": [
        {"terms": {"store_id": [165]}}
      ]
    }
  }
}

 

 

设置fieldData

// 第一步,创建索引 (如果已经有索引,直接看第二步)
PUT mysql-shop_trades-order_statics2
{
  "mappings": {
    "_doc": {
      "properties": {
        "entity_store_id": { 
          "type": "text",
          "fields": {
            "keyword": { 
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

// 第二步 设置fieldData为true
PUT mysql-shop_trades-order_statics/_mapping/_doc
{
  "properties": {
    "entity_store_id": {
      "type":     "text",
      "fielddata": true
    }
  }
}

// 第三步 可以查看该索引的Mapping结构,fieldData是否加上去
{
  "mapping": {
    "doc": {
      "properties": {
        "entity_store_id": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          },
          "fielddata": true
        }
      }
    }
  }
}

延伸

 

 

设置 max_result_window

PUT /mysql-shop_trades-order_statics/_settings
{
  "index": {
    "max_result_window": 100000
  }
}

 

Spring Boot 入门

一、开发基础

  • Java基础(两到三小时过一遍)
  • Java开发环境配置必须使用JDK1.8
  • IDE安装(优先使用IntelliJ IDEA)

二、名词解释

  • Spring:JAVA开发应用框架
  • Spring Boot:用来简化Spring应用的初始搭建以及开发过程的配置框架
  • Maven:Java项目构建工具,成熟的项目
  • Gradle:更简洁的Java项目构建工具,吸收了旧构建工具的优点。
  • JPA:是Sun官方提出的Java持久化规范,即数据库操作规范。
  • Hibernate:Hibernate是一个ORM框架,是JPA的默认实现方式,一般说JPA都是指Hibernate。
  • Mybatis:Mybatis是一个轻便的ORM框架。
  • Spring data jpa:是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架

三、新项目流程

  1. 新建gradle项目
    • File->new->Prroject->Spring Initializr

    • 填写Group、Artifact选择Gradle Project项目生成

    • 可以直接在生成项目的时候选择对应需要安装的插件,如:web、jpa、mybatis等,也可以在项目初始化完成之后在build.gradle中添加/配置

  2. 配置build.gradle(位于根目录)

    plugins {
        id 'org.springframework.boot' version '2.1.3.RELEASE'
        id 'java'
    }
    
    apply plugin: 'io.spring.dependency-management'
    
    group = 'com.duomai'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '1.8' // JDK最大兼容版本
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        // Spring Boot JPA 组件
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        // Spring Boot Web组件
        implementation 'org.springframework.boot:spring-boot-starter-web'
        // Mybatis插件,注意暂时使用**1.1.1**版本,高版本的运行好像有问题
        implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.1.1'
        // Mybatis分页插件
        implementation group: 'com.github.pagehelper', name: 'pagehelper-spring-boot-starter', version: '1.2.10'
        runtimeOnly 'org.springframework.boot:spring-boot-devtools'
        runtimeOnly 'mysql:mysql-connector-java'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }
    
    

    修改了build.gradle后,idea会自动安装/更新依赖包。
    参考:gradle官网Spring Boot Web服务搭建Spring Boot Mysql使用Spring Boot JPA使用

  3. 项目基础配置(位于src/resources/application.properties

    #运行配置
    server.port=9000
    #数据格式配置
    spring.jackson.time-zone=GMT+8 // 设置接口返回时区为东八区
    spring.jackson.date-format=yyyy-MM-dd HH:mm:ss // 自动将接口返回中的日期格式转换为标准格式
    #数据库连接配置
    spring.datasource.url=jdbc:mysql://192.168.0.235:3355/shop_balances?serverTimezone=Asia/Shanghai&tinyInt1isBit=false // serverTimezone选择Mysql东八区,tinyInt1isBit禁止Mysql自动将tinyint(1)类型数据映射为boolean类型
    spring.datasource.username=cishop
    spring.datasource.password=fuyuan1906
    #log配置
    logging.path=E:/java/demo/balance_card/log
    logging.level.com.favorites=DEBUG
    #logging.level.org.springframework.web=INFO
    logging.level.org.hibernate=ERROR
    #mybatis设置
    mybatis.type-aliases-package=com.duomai.balance_card.Model.Mapper
    mybatis.configuration.map-underscore-to-camel-case=true
    logging.level.com.duomai.balance_card.Model.Mapper=DEBUG
    #pagehelper插件设置
    pagehelper.helperDialect=mysql
    pagehelper.reasonable=false
    pagehelper.supportMethodsArguments=true
    pagehelper.params=pageNum=page;pageSize=limit
    #jpa 设置
    spring.jpa.properties.hibernate.hbm2ddl.auto=update
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
    spring.jpa.show-sql= true
    
  4. 项目启动
    • IDEA启动,运行[Prefix]Application.java文件即可

    • 命令行启动

      • Maven项目
        在springboot的应用的根目录下运行 mvn spring-boot:run

      • Grdale项目
        在springboot的应用的根目录下运行 gradle bootRungradlew bootRun
        (前者是使用本地的gradle版本运行,后者是使用代码仓库中的gradle运行)

    • 打包构建可执行文件运行

  5. 开发环境热更新设置
    热更新不是很好用,有一定的延迟时间

四、项目文件分层解析

// 业务代码 src/main
java 
    com
        duomai
            balance_card
                [Prefix]Application.java // 项目启动文件,可以做一些全局设置,如时区设置、Mapper扫描等
                Config // 配置类,用于注册一些全局配置,如拦截器注册等
                Middleware // 中间件,实现AOP功能
                Controller // 控制器,主要做路由功能
                    xxxController.java
                    BaseErrorController.java // 路由匹配失败时使用的控制器
                Service // 业务代码
                Model // 目录主要用于实体与数据访问层
                    Entity // 数据表实体类
                    Repository // JPA数据仓库
                    Mapper // Mybatis映射文件
                    Provider // Mapper的Sql生成器
                Library // 库类,存放公共类文件/纯定义文件等
                    ApiReturnDefines.java // 接口返回定义
                    ExceptionErrprDefines.java // 异常监听层定义
                Helper // 辅助函数类文件
                OutPut // 接口输出层
                    ApiResult.java // 最终的接口输出格式
                    AiReturn.java // 快速生成ApiResult类,供外部调用
                Exception // 统一的异常处理
                    ControllerHandler // 路由层异常监听
                    SqlHandler // 数据库层异常监听
// 配置项
resources
    appliaction.properties // 项目配置文件

五、控制器中间件

  1. 一般使用Spring过滤器或拦截器实现AOP切面编程

  2. 过滤器和拦截器的对比

    • 作用域不同
      过滤器依赖于servlet容器,只能在 servlet容器,web环境下使用。
      拦截器依赖于spring容器,可以在spring容器中调用,不管此时Spring处于什么环境。
    • 细粒度的不同
      过滤器的控制比较粗,只能在请求进来时进行处理,对请求和响应进行包装。
      拦截器提供更精细的控制,可以分为controller对请求处理之前、渲染视图之后、请求处理之后三个切面。
    • 中断链执行的难易程度不同
      拦截器可以 preHandle方法内返回 false 进行中断。
      过滤器就比较复杂,需要处理请求和响应对象来引发中断,需要额外的动作,比如将用户重定向到错误页面。
  3. 拦截器的使用
    • 编写自定义拦截器(Middleware/ControllerInterceptor.java
    public class ControllerInterceptor implements HandlerInterceptor {
        private Logger logger = LoggerFactory.getLogger(ControllerInterceptor.class);
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            logger.info("preHandle....");
            // token校验等
            String token = request.getHeader("token");
            //     Common.sendJson(response, ApiReturn.fail(1001, "token验证失败"));
            //      return false;
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            logger.info("postHandle...");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            // 接口日志记录等
            logger.info("afterCompletion...");
        }
     ```
    
    **说明**:
    preHandle:对客户端发过来的请求进行前置处理,如果方法返回true,继续执行后续操作,如果返回false,执行中断请求处理,请求不会发送到Controller。可以在这里校验一些权限信息,如token等,校验失败直接以JSON格式返回请求。
    
    postHandler:在请求进行处理后执行,也就是在Controller方法调用之后处理,前提是preHandle方法返回true。具体来说,postHandler方法会在DispatcherServlet进行视图返回渲染前被调用。
    
    afterCompletion: 该方法在整个请求结束之后执行,前提依然是preHandle方法的返回值为true。
    
    • 注册拦截器(Config/InterceptorConfig.java
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        // 核心方法
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(ControllerInterceptor())
                    //配置拦截规则
                    .addPathPatterns("/**")
                    .order(1);
    
            // 多个拦截器按上述方法持续注册即可,同时也可以设置order值,从小到大执行。
        }
    
        @Bean
        public HandlerInterceptor ControllerInterceptor() {
            return new ControllerInterceptor();
        }
    }
    

六、Controller层

  1. 定义控制器文件
    @RestController
    在类文件头部定义,标明为控制器文件,且输出格式为JSON
    
  2. 路由和参数
    1. 定义路由名称,接收方法
      例:@RequestMapping(value = "/get", method = {RequestMethod.GET, RequestMethod.POST})
      
        可选参数:
      
        value:路由名称
      
        method:指定请求的method类型, GET、POST、PUT、DELETE、PATCH等,可多选
      
        consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html
      
        produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
      
        params: 指定request中必须包含某些参数值
      
        headers: 指定request中必须包含某些指定的header值
      
    2. 参数接收
      例:@RequestParam(value = "fields", required = false, defaultValue = "*") String fields
      
          value:参数名称
      
          defaultValue:默认值
      
          required:是否是必要参数
      
    3. 自定义错误路由
      1. 在Controller层中添加BaseErrorController.java文件,用于监听路由匹配失败的情况
        @Controller
        public class BaseErrorController implements ErrorController {
            @Override
            public String getErrorPath() {
                System.out.print("错误页面");
                return "error/error";
        }
        
            @RequestMapping(value = "/error")
            public void error() throws Exception {
                throw new Exception("路由匹配失败");
            }
        }
        
      2. 在Exception文件夹中添加ControllerHandler.java,用于捕获路由报错并输出。
        @RestControllerAdvice
        public class ControllerHandler {
            // 缺少必选参数
            @ExceptionHandler({MissingServletRequestParameterException.class})
            @ResponseBody
            public ApiResult requestMissingServletRequest(MissingServletRequestParameterException e){
                return ApiReturn.fail(ExceptionErrorDefines.RequestMissingServletRequest, e.getMessage());
            }
        }
        未解决:抛出异常后访问404页面运行环境会报错,但是页面正常  
        

        参考:https://www.jianshu.com/p/393f70b55b1b

  3. Service层调用

    1. Service类成员注入
      • 使用@Autowired修饰符进行依赖注入
        @Autowired
        private final CardService cardService;
        
      • 用构造函数来做注入类成员(推荐使用)
        private StoreBalanceCardsRepository cardsRepository;
        public CardController(StoreBalanceCardsRepository cardsRepository) {
            this.cardsRepository = cardsRepository;
        }
        **注**:
        IntelliJ IDEA使用依赖注入会有IDE报错,但不影响实际编译运行,如需去除报错提示,需要在Dao层(Respository/Mapper)类开头添加注解 `@Repository`
        
    2. 调用
      cardService.get(id, fields);
      

七、Service层

  1. 定义Service文件
    @Service
    在类文件头部定义,标明为Service文件
    
  2. 注入Model层操作文件
    private final StoreBalanceCardsRepository storeBalanceCardsRepository;
    private final StoreBalanceCardsMapper storeBalanceCardsMapper;
    
        CardService(StoreBalanceCardsRepository storeBalanceCardsRepository, StoreBalanceCardsMapper storeBalanceCardsMapper) {
            this.storeBalanceCardsRepository = storeBalanceCardsRepository;
            this.storeBalanceCardsMapper = storeBalanceCardsMapper;
        }
    
  3. 调用Model层文件

八、Model层

  1. Repository

    • Spring中概念,概念类似于数据仓库,是Spring data jpa的实现。居于业务层和数据层之间,将两者隔离开来,在它的内部封装了数据查询和存储的逻辑。

    • Repository和传统意义上的DAO的区别:
      Repository蕴含着真正的OO概念,即一个数据仓库角色,负责所有对象的持久化管理。DAO则没有摆脱数据的影子,仍然停留在数据操作的层面上。Repository是相对对象而言,DAO则是相对数据库而言,虽然可能是同一个东西,但侧重点完全不同。

  2. Mapper

    存放Mybatis数据库关系映射方法

  3. Provider
    为Mapper层提供的SQL生成器,即将SQL的生成与映射解耦。

  4. Entity

    • 根据表结构自动生成实体类
      **注意**:
      i、多次自动生成不会覆盖,如需更新需要把旧的实体类文件删除
      
      ii、有需要的话可以在实体类文件右击generate一键生成构造函数、set get方法、@Autowired等
      
      iii、自动生成的实体类中datetime类型的字段会被转为Java的timestamp类型数据,存储的时候也使用timestamp类型即可
      
      iv、生成出来的实体类catalog = "" 报错,是IDE的报错,不影响使用
      

      参考文档:https://blog.csdn.net/chenju05244554/article/details/1009142081

  5. Hibernate 和 Mybatis 的对比

    • Hibernate优势:
      i、DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
      
      ii、对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
      
      iii、数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
      
      iv、有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。
      
    • Mybatis优势:
      i、MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
      
      ii、MyBatis容易掌握,而Hibernate门槛较高。
      
    • 选用Mybatis的原因
      i、Hibernate无法满足动态获取部分字段的需求,即使是使用Hibernate提供的原始SQL也无法实现
      
      ii、Hibernate的JPA查询只适用于一些简单的情况,如遇到复杂的SQL,Repository中的方法名会很长。这时候又将回到Hibernate的自定义SQL查询,即原生SQL。
      
      iii、Hibernate的维护成本比MyBatis高很多,MyBatis的SQL生成完全取决于开发者,所以SQL修改、维护、优化会比较便利。
      

九、MyBatis使用

  1. 引入MyBatis以及pagehelper分页插件
    dependencies {
        implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.1.1'
        implementation group: 'com.github.pagehelper', name: 'pagehelper-spring-boot-starter', version: '1.2.10'
    }
    
  2. application.properties 添加相关配置
    #mybatis设置
    mybatis.type-aliases-package=com.duomai.balance_card.Model.Mapper // 项目中Mapper存放的包
    mybatis.configuration.map-underscore-to-camel-case=true // 自动将sql字段下划线转为驼峰,可以保证取出的数据格式就是数据库中存储的格式
    logging.level.com.duomai.balance_card.Model.Mapper=DEBUG // 开启DEBUG模式,用于开发环境,记录执行的SQL。
    #pagehelper插件设置
    pagehelper.helperDialect=mysql // 指定分页插件使用哪种数据库
    pagehelper.reasonable=false // 分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
    pagehelper.supportMethodsArguments=true // 支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页,设置后无需手动启用分页插件。
    pagehelper.params=pageNum=page;pageSize=limit //  自定义Mapper 接口参数来传递分页参数的参数名
    
  3. 添加Mapper类扫描的两种方式
    • 在启动类中添加对mapper包扫描@MapperScan(推荐使用)
      @SpringBootApplication
      @MapperScan("com.duomai.balance_card.Model.Mapper")
      
    • 在具体的Mapper类上面添加注解 @Mapper

  4. Mapper类SQL用例

    public interface StoreBalanceCardsMapper {
        // select用例
        @SelectProvider(type = StoreBalanceCardsSqlBuilder.class, method = "getById")
        List<Map> getById(@Param("id") int id, @Param("fields") String fields, @Param("page") int page, @Param("limit") int limit);
    
        // 列表
        @SelectProvider(type = StoreBalanceCardsSqlBuilder.class, method = "list")
        Page<Map> list(@Param("fields") String fields, @Param("page") int page, @Param("limit") int limit);
    
        // insert用例
        @InsertProvider(type = StoreBalanceCardsSqlBuilder.class, method = "add")
        @Options(useGeneratedKeys=true, keyProperty="storeBalanceCard.id", keyColumn="id")
        int add(@Param("storeBalanceCard") StoreBalanceCards storeBalanceCard);
    
        // update用例
        @UpdateProvider(type = StoreBalanceCardsSqlBuilder.class, method = "updateName")
        @Options(useGeneratedKeys=true, keyProperty="storeBalanceCard.id", keyColumn="id")
        int updateName(@Param("storeBalanceCard") StoreBalanceCards storeBalanceCard, @Param("limit") int limit);
    
        // delete用例
        @DeleteProvider(type = StoreBalanceCardsSqlBuilder.class, method = "deleteById")
        int delete(@Param("id") int id, @Param("limit") int limit);
    }
    
    • 通过Provider的方式动态获取SQL

    • 需要分页时在具体的方法后多加page、limit参数,自动实现分页

      // Service层调用
      List storeBalanceCard = storeBalanceCardsMapper.list(fields, page, limit);
      // Mapper层实现
      Page<Map> list(@Param("fields") String fields, @Param("page") int page, @Param("limit") int limit);
      
    • 分页组件仅可用于查询,不可用于更新/删除,更新/删除需要另外实现

  5. Providerle类SQL用例

    public class StoreBalanceCardsSqlBuilder {
        private static final String STORE_BALANCE_CARDS = "store_balance_cards";
        public static String getById(int id, String fields) {
            return new SQL(){ { 
                SELECT(fields);
                FROM(STORE_BALANCE_CARDS);
                WHERE("store_balance_cards.id = #{id}");
            } }.toString();
        }
    
        public static String list(String fields) {
            return new SQL(){ {
                SELECT(fields);
                FROM(STORE_BALANCE_CARDS);
            } }.toString();
        }
    
        public static String add(@Param("storeBalanceCard") StoreBalanceCards storeBalanceCard) {
            return new SQL(){ {
                INSERT_INTO(STORE_BALANCE_CARDS);
                VALUES("sys_name", "#{storeBalanceCard.sysName}");
                VALUES("store_id", "#{storeBalanceCard.storeId}");
                VALUES("entity_store_id", "#{storeBalanceCard.entityStoreId}");
                VALUES("name", "#{storeBalanceCard.name}");
                VALUES("type", "#{storeBalanceCard.type}");
                VALUES("item_id", "#{storeBalanceCard.itemId}");
                VALUES("photo", "#{storeBalanceCard.photo}");
                VALUES("state", "#{storeBalanceCard.state}");
                VALUES("upgrade", "#{storeBalanceCard.upgrade}");
                VALUES("recharge_discount", "#{storeBalanceCard.rechargeDiscount}");
                VALUES("updated_at", "#{storeBalanceCard.updatedAt}");
                VALUES("created_at", "#{storeBalanceCard.createdAt}");
            } }.toString();
        }
    
        public static String updateName(@Param("storeBalanceCard") StoreBalanceCards storeBalanceCard, @Param("limit") int limit) {
            return new SQL(){ {
                UPDATE(STORE_BALANCE_CARDS);
                SET("name=#{storeBalanceCard.name}");
                if (storeBalanceCard.getUpdatedAt() != null) {
                    SET("updated_at=#{storeBalanceCard.updatedAt}");
                }
                WHERE("store_id = #{storeBalanceCard.storeId}");
            } }.toString() + " limit " + limit;
        }
    
        public static String deleteById(int id, int limit) {
            return new SQL(){ {
                DELETE_FROM(STORE_BALANCE_CARDS);
                WHERE("store_balance_cards.id = #{id}");
            } }.toString() + " limit " + limit;
        }
    }
    
  6. 分页组件详解

十、接口输出

  1. 接口统一输出格式

    state: // 状态位
    msg: // 接口输出提示信息
    data: { // 总的接口输出数据,可以为空
        data: // 接口数据
        other: // 其他返回数据,如total/page_total等
        ...:
    }
    
  2. 正常接口输出/异常监听输出统一使用OutPut/ApiReturn方法

十一、异常处理

  1. 项目运行异常统一监听
    现有:ControllrHandler 路由异常监听、SqlHandler 数据库操作异常监听
    需要持续添加
  2. 统一使用接口输出类进行返回,杜绝直接返回报错信息。

十二、单元测试

  1. 添加单元测试类

    src\test\java\com\duomai\balance_card\BalanceCardApplicationTests.java

  2. demo

    @RunWith(SpringRunner.class)
    @SpringBootTest
    @AutoConfigureMockMvc
    public class BalanceCardApplicationTests {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        @SuppressWarnings("unchecked")
        public void getCard() throws Exception {
            String card_id = "1";
            String fields = "store_id,item_id,recharge_discount,photo,type,name";
            String res = this.mockMvc.perform(get("/card/get")
                    .param("id", card_id).param("fields", fields)
                    )
                    .andDo(print())
                    .andExpect(status().isOk())
                    .andReturn()
                    .getResponse()
                    .getContentAsString();
           // 接口返回不为空
           assertThat(res).isNotNull();
           // 校验接口返回格式是否完整
           Map<String, Object> api_res = Common.jsonToMap(res);
           assertThat(api_res).isNotNull();
           assertThat(api_res).hasSize(3);
           assertThat(api_res).containsKeys("state", "msg", "data");
           // 校验接口返回state是否正确
           int state = (int) api_res.get("state");
           Map<String, Object> api_data = (HashMap<String, Object>) api_res.get("data");
           assertThat(state).isEqualTo(ApiReturnDefines.SUCCESS);
           // 校验data数据是否正确
           assertThat(api_data).containsOnlyKeys("data");
           ArrayList<Map> data = (ArrayList) api_data.get("data");
           String[] fieldsAll = fields.split(",");
           assertThat(data).hasSize(1);
           assertThat(data.get(0)).containsKeys((Object[]) fieldsAll);
        }
    }
    
  3. 详解
    • 使用 @RunWith(SpringRunner.class)@SpringBootTest 定义测试类
    • 添加注解 @AutoConfigureMockMvc ,使用 Spring MockMvc 模拟Spring的HTTP请求并将其交给控制器,实际上并没有真正地启动服务器,仅仅是Mock,节省了启动服务器的开销。
    • 使用 AssertJ 库类来验证接口返回内容
      在demo中,使用断言验证了接口返回格式、state状态、data数据格式等基本内容。

    参考文档:https://spring.io/guides/gs/testing-web/

十三、项目打包

  1. 不同的构建文件
  • 普通 jar 包 : 会将源码编译后以工具包(即将class打成jar包)的形式对外提供,此时,你的 jar 包不一定要是可执行的,只要能通过编译,可以被别的项目以 import 的方式调用。
  • 可执行 jar 包 : 能通过 java -jar 的命令运行。
  • 普通 war 包 : war 是一个 web 模块,其中包括 WEB-INF,是可以直接运行的 WEB 模块。做好一个 web 应用后,打成包部署到容器中。
    • 可执行 war 包 : 普通 war 包 + 内嵌容器 。
  1. 构建可执行 jar 包

    • IDE打包
      右侧Gradle -> Tasks -> build -> build
    • 命令行打包
      进入项目根目录,执行gradle build
  2. 运行可执行 jar 包
    java -jar build/libs/balance_card-0.0.1.jar
    运行时可带参数,同application.properties中的参数名
    例:

    java -Djava.security.egd=file:/dev/./urandom -jar /opt/ci123/www/html/java/balance_card/build/libs/${jarName}.jar --server.port=80
    

十四、容器化部署

  1. 创建docker镜像
  • Dockfile 编写

    FROM java:8
    MAINTAINER duomai
    # 设置镜像源
    COPY sources.list /etc/apt/sources.list
    # 安装扩展
    RUN apt-get update && apt-get install -y \
    wget \
    curl \
    vim \
    git \
    less
    # 配置系统时间
    RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo 'Asia/Shanghai' > /etc/timezone
    VOLUME /tmp
    # 设置工作目录
    ADD . /opt/ci123/www/html/java
    WORKDIR /opt/ci123/www/html/java
    # 指定输出端口
    EXPOSE 80

    • 快速构建镜像

      sh docker/build.sh [version]

  1. 环境变量配置
  • 配置

    #数据库连接配置 application.properties
    spring.datasource.url=jdbc:mysql://${APP_DB_HOST:192.168.0.235}:${APP_DB_PORT:3355}/${APP_DB_DATABASE:shop_balances}
    spring.datasource.username=${APP_DB_USER:cishop}
    spring.datasource.password=${APP_DB_PASSWORD:fuyuan1906}

    1. 启动容器( docker/run.sh )
  • 启动参数

    -p // 宿主机运行端口
    -d // 宿主机项目地址,缺省为执行run.sh的上一级目录
    -n // 容器名称
    -v // 镜像版本号
    -j // jar包版本,缺省为1.0

  • 环境变量

    # 运行配置
    APP_SERVER_PORT // 容器运行jar包的端口
    # 数据库配置
    APP_DB_HOST
    APP_DB_PORT
    APP_DB_DATABASE
    APP_DB_USER
    APP_DB_PASSWORD
    # 日志配置
    APP_LOG_PATH // 日志输出地址
    APP_LOG_SPRING_WEB_LEVEL // 对应logging.level.org.springframework.web,指org.springframework.web这个包下的日志输出等级,默认为ERROR,开发环境可配置为DEBUG
    APP_LOG_MYBATIS_LEVEL // 对应logging.level.com.duomai.balance_card.Model.Mapper,指balance_card.Model.Mapper包下的日志输出等级,默认为ERROR,开发环境可配置为DEBUG,开启DEBUG之后可在日志中查看DB操作

  • 启动容器

    docker run -d \
    -e APP_SERVER_PORT=8080 \
    -e APP_DB_HOST=192.168.0.235 \
    -e APP_DB_PORT=3355 \
    -e APP_DB_DATABASE=shop_balances \
    -e APP_DB_USER=cishop \
    -e APP_DB_PASSWORD=fuyuan1906 \
    -e APP_LOG_PATH=/opt/ci123/www/html/java/balance_card/log \
    -e APP_LOG_SPRING_WEB_LEVEL=DEBUG \
    -e APP_LOG_MYBATIS_LEVEL=DEBUG \
    -p $port:80 \
    -v $dir:/opt/ci123/www/html/java/balance_card \
    --restart=always \
    --name $name \
    harbor.oneitfarm.com/duomai/java-balance_card:$version \
    sh /opt/ci123/www/html/java/balance_card/docker/start.sh -j $jarName