Підключення до бази даних - чи повинні вони передаватися як параметр?


11

У нас є система, згідно з якою підключення до бази даних отримується один раз за допомогою загального методу і передається через відповідний клас, який буде використовуватися. Є сумніви, що передача підключення до бази даних як параметр до різних класів спричинить проблему, тому я перевіряю тут, чи є це насправді життєздатним, і чи є кращі зразки для цього?

Я знаю, що є кілька інструментів ORM, щоб зробити наполегливість, але ми все ще не можемо вступити в це.

Будь-який відгук вітається, дякую.


Які проблеми ви маєте на увазі? У кого є ці сумніви? (Не ви, я припускаю.)
Грег Х'югілл

1
Такі проблеми, як змушення забудовника забути закрити з'єднання, я взагалі просто намагаюся зрозуміти, чи є хорошою практикою передавати підключення до бази даних до різних методів як параметр. У Я. сумніви походять від іншого розробника.
ipohfly

Відповіді:


8

Так, безпечно пройти навколо зв'язку. Ви обробляєте з'єднання у зовнішньому блоці управління. У цьому немає нічого небезпечного.

Небезпечно - це написання коду, який не гарантує належне розміщення з'єднання. Забуття очистити ресурс не пов'язане з передачею його навколо. Ви можете так само легко написати код, який залишає звисне з'єднання, не передаючи його нікуди.

У C ++ ви захищені RAII, якщо ви виділяєте в стеку або використовуєте смарт-покажчики. У C # складіть жорстке правило, що всі одноразові об'єкти (наприклад, з'єднання) оголошуються в блоці "використовуючи". У Java прибирайте за допомогою логіки спробу. Проведіть огляд коду на весь код рівня даних, щоб забезпечити це.

Найбільш поширений випадок використання, коли у вас є кілька операцій, які можна комбінувати в багатьох перестановках. І кожна з цих перестановок повинна бути атомною транзакцією (всі успішні або відкатні). тоді вам слід передати транзакцію (а отже, і відповідне з'єднання) навколо всіх методів.

Припустимо, у нас є багато дій foobar (), які можна комбінувати різними способами як атомні транзакції.

//example in C#
//outer controlling block handles clean up via scoping with "using" blocks.
using (IDbConnection conn = getConn())
{
    conn.Open();
    using (IDbTransaction tran = conn.BeginTransaction())
    {
        try
        {//inner foobar actions just do their thing. They don't need to clean up.
            foobar1(tran);
            foobar2(tran);
            foobar3(tran);
            tran.Commit();
        }
        catch (Exception ex)
        { tran.Rollback(); }
    }
}//connection is returned to the pool automatically

До речі, ви хочете відкрити з'єднання якомога пізніше, розпоряджайтеся ними якнайшвидше. Ваші товариші по команді можуть мати рацію, якщо ви ставитесь до з'єднань як до членів об'єкта, представляючи їх як непотрібний стан і залишаючи з'єднання відкритими набагато довше, ніж потрібно. Але акт передачі з'єднання або транзакції як параметр не є за своєю суттю помилковим.

До речі. Залежно від підтримки вашої мови для функцій першого класу, ви можете здійснити список дій foobar (). Таким чином, одна функція могла обробляти всі перестановки дій. Усунення дублювання зовнішнього керуючого блоку для кожної перестановки.


позначивши це як відповідь, оскільки це дає мені більше уявлення про те, як склалася ситуація
ipohfly

6

Це здається, що ви після ін'єкції залежності . Тобто, об'єднане з'єднання створюється один раз і вводиться там, де це потрібно. Безумовно, проходження з'єднання через параметр методу - це один із способів введення залежності, але контейнер IoC, такий як Guice, PicoContainer або Spring - це інший (безпечніший) спосіб зробити це.

Використання DI означає, що ви можете акуратно завершити логіку навколо створення, відкриття, використання та закриття з'єднання - далеко від вашої основної бізнес-логіки.

Spring JDBC та інші - це більш прикладні приклади виконання подібної поведінки для вас


Емм не дуже дивиться на ін'єкцію залежності. Просто намагаюся з’ясувати, чи це взагалі хороша практика робити це, і якщо це не так, то який кращий спосіб керування підключенням до бази даних (хоча це і є один із способів DI).
ipohfly

-1. Одне з'єднання не підходить для багатокористувацької системи. Може здатися, що він працює через низький обсяг користувача та швидке виконання. За допомогою об'єднання краще інстанціювати об’єкт з'єднання за дією навіть у єдиній системі користувача.
mike30

2

Проходження навколо баз даних, а не даних може призвести до проблем. У цій мірі, коли це практично, не передайте базу даних, якщо тільки не можна гарантувати належну гігієну бази даних.

Проблема при обходженні речей із бази даних полягає в тому, що вона може бути неохайною. Я бачив більше однієї помилки в коді з тим, хто проходить навколо з’єднання з базою даних, коли хтось захоплює набір результатів і зберігає в локальному об'єкті (набір результатів, все ще підключений до бази даних), а потім зв'язує курсор у база даних протягом значного часу. Інший екземпляр хтось передав набір результатів комусь іншому (який тоді був схований), а потім метод, який передав набір результатів, закрив його (і заяву), що призвело до помилок, коли інші методи намагалися працювати з набором результатів, який більше не було.

Все це випливає з неповаги до бази даних, з'єднання, заяви, набору результатів та їх життєвих циклів.

Щоб уникнути цього, існують існуючі шаблони та структури, які краще грають з базами даних і не мають речей із базами даних, щоб вийти з класів, в яких вони обмежені. Дані надходять, дані виходять, база даних залишається поставленою.


1
+1 db-з'єднання повинні мати найкоротший проміжок часу. Відкрийте її, використовуйте, закрийте якомога швидше. На сьогоднішній день існує маса реалізацій пулу з'єднань, тому використання з'єднання для декількох операцій є помилковою економією. І запрошення для помилок або проблем із виконанням (тримання замків на таблицях, використання ресурсів підключення)
jqa

Як називаються деякі з цих існуючих моделей та структур?
Даніель Каплан

@tieTYT Основними з них, які виходять на перший план, є об’єкт доступу до даних, який служить для приховування бази даних від решти програми. Дивіться також Шар доступу до даних та Об'єктно-реляційне картографування

Коли я думаю про ці структури, я відчуваю, що вони знаходяться на більш високому рівні абстракції, ніж те, про що він питає. Скажімо, у вас є спосіб отримати Rootвід Dao. Але тоді ти розумієш, що ти теж хочеш отримати спосіб, Nodeне витягаючи з нього весь Rootоб’єкт. Як зробити так, щоб RootДао називав Nodeкод Дао (тобто повторне використання), але переконайтесь, що NodeДао закриває з'єднання лише тоді, коли NodeДао безпосередньо викликається, і тримає з'єднання відкритим, коли RootДао викликається?
Даніель Каплан

1
Просто хотілося додати, що якщо ви не перебуваєте в режимі автоматичної фіксації, передача з'єднання навколо може призвести до ситуації, коли один об’єкт оновлює базу даних, то інший (можливо, не пов'язаний) об'єкт отримує з'єднання, має помилку і завершує роботу відкат змін першого об'єкта. Ці типи помилок можуть бути дуже важкими для налагодження.
TMN

2

Передача Connectionпримірників навколо зазвичай не є проблемою, хоча в більшості ситуацій тільки реалізація DAO повинна мати щось спільне з ними. Тепер, якщо ваша проблема полягає в тому, що з'єднання не будуть закриті після використання, це насправді легко виправити: Connectionоб’єкт потрібно закрити на тому ж рівні, на який він відкрився, тобто тим же методом. Я особисто використовую таку схему коду:

final Connection cnx = dataSource.getConnection();
try {
    // Operations using the instance
} finally {
    cnx.close();
}

Таким чином я забезпечую, щоб усі з'єднання були завжди закритими, навіть якщо виняток буде передано всередині блоку. Я фактично іду до тих пір, поки використовую точно таку ж схему для Statementі ResultSetекземплярів, і все поки плавне плавання досі.

Редагувати 2018-03-29: Як зазначено користувачем1156544 у коментарях нижче, починаючи з Java 7, слід використовувати перевагу конструкції спробувати ресурсів. Використовуючи його, шаблон коду, який я надав у своїй початковій відповіді, можна спростити так:

try (final Connection cnx = dataSource.getConnection()) {
    // Operations using the instance
}

1
Я використовую щось подібне. У мене є функція doInTransaction (завдання DbTask), де DbTask - мій інтерфейс із методом із параметром з'єднання. doInTransaction отримує з'єднання, викликає завдання та здійснює (або відкат, якщо був виняток) і закриває це з'єднання.
user470365

судячи з вашого прикладу, це означало б, що об’єкт DataSource є однотонним?
ipohfly

@ipohfly Насправді я мав би назвати цей об’єкт, dataSourceа не DataSource(я виправлю свою відповідь щодо цього питання). Точний тип цього об'єкта був би javax.sql.DataSource. У старому коді я мав одиночне управління всіма доступними джерелами даних у своїх додатках. Мої DAO не повинні були знати про це через те, що DataSourceекземпляр забезпечується через ін'єкцію залежності.
KevinLH

Якщо ви використовуєте цю схему, краще скористайтеся спробу використання ресурсів
user1156544

Ще коли я відповів, я ще не використовував Java 7. Але ви маєте рацію, що саме в цей час це має бути найкращим способом. Я оновлю свою відповідь, щоб включити вашу пропозицію.
KevinLH

0

є компроміс робити речі таким чином, а не використовувати сингл, який можна отримати за потреби. У минулому я робив речі обома способами.

Загалом, вам потрібно подумати про наслідки управління підключеннями до бази даних, і це може бути, а може бути, не ортогональним для використання запитів до бази даних. Наприклад, якщо у вас є одне db-з'єднання для даного екземпляра програми, і воно закривається, коли не використовується, це буде ортогональним. Поставте управління в однотонний клас і не передайте його. Це дозволяє керувати db-з'єднанням у міру необхідності. Наприклад, якщо ви хочете закрити з'єднання під час кожної комісії (і повторно відкрити при наступному дзвінку), це зробити простіше в одиночному режимі, оскільки API для цього може бути централізованим.

З іншого боку, припустимо, вам потрібно керувати пулом з'єднань, де для даного дзвінка може знадобитися будь-яке довільне з'єднання. Це може статися, наприклад, при здійсненні розподілених транзакцій на декількох серверах. У цьому випадку вам зазвичай набагато краще передавати об’єкт db-з'єднання, ніж ви працюєте з одинаковими кнопками. Я думаю, що це звичайно рідше, але немає нічого поганого в тому, коли робити це потрібно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.