การเชื่อมต่อฐานข้อมูล - ควรส่งผ่านเป็นพารามิเตอร์หรือไม่


11

เรามีระบบที่การเชื่อมต่อฐานข้อมูลจะได้รับเพียงครั้งเดียวโดยใช้วิธีการทั่วไปและจะถูกส่งไปทั่วคลาสที่เกี่ยวข้องที่จะใช้ มีข้อสงสัยว่าการผ่านการเชื่อมต่อฐานข้อมูลเป็นพารามิเตอร์ไปยังคลาสที่แตกต่างกันจะทำให้เกิดปัญหาดังนั้นฉันกำลังตรวจสอบที่นี่เพื่อดูว่าสิ่งนี้สามารถใช้งานได้จริงหรือไม่และมีรูปแบบที่ดีกว่านี้หรือไม่

ฉันรู้ว่ามีเครื่องมือ ORM บางอย่างในการคงอยู่ แต่เราไม่สามารถทำเช่นนั้นได้ ..

ยินดีรับข้อเสนอแนะใด ๆ ขอบคุณ


คุณหมายถึงปัญหาประเภทใด ใครมีข้อสงสัยเหล่านี้ (ไม่ใช่คุณฉันคิดเอาเอง)
Greg Hewgill

1
ปัญหาเช่นทำให้ผู้พัฒนาลืมที่จะปิดการเชื่อมต่อโดยทั่วไปฉันแค่พยายามที่จะดูว่ามันเป็นวิธีที่ดีในการส่งผ่านการเชื่อมต่อฐานข้อมูลไปยังวิธีการต่าง ๆ เป็นพารามิเตอร์ Ya ข้อสงสัยมาจากผู้พัฒนารายอื่น
ipohfly

คำตอบ:


8

ใช่มันปลอดภัยที่จะผ่านการเชื่อมต่อ คุณจัดการการเชื่อมต่อในบล็อกควบคุมภายนอก ไม่มีอะไรที่ไม่ปลอดภัยเกี่ยวกับเรื่องนี้

สิ่งที่ไม่ปลอดภัยคือการเขียนรหัสที่ไม่รับประกันว่าการเชื่อมต่อจะถูกกำจัดอย่างเหมาะสมในเวลาที่เหมาะสม การลืมทำความสะอาดทรัพยากรนั้นไม่มีความเกี่ยวข้องกับการผ่านไปมา คุณสามารถเขียนโค้ดที่ทำให้การเชื่อมต่อแบบแฮงค์โดยไม่ผ่านมันไปไหน

ใน C ++ คุณได้รับการคุ้มครองโดย RAII ถ้าคุณจัดสรรในกองซ้อนหรือใช้ตัวชี้สมาร์ท ใน C # สร้างกฎอย่างหนักที่วัตถุที่ใช้แล้วทิ้งทั้งหมด (เช่นการเชื่อมต่อ) จะถูกประกาศในบล็อก "using" ใน 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

BTW คุณจะต้องเปิดการเชื่อมต่อให้ช้าที่สุดเท่าที่จะทำได้ให้กำจัดพวกเขาโดยเร็วที่สุด เพื่อนร่วมทีมของคุณอาจถูกต้องหากคุณปฏิบัติต่อการเชื่อมต่อในฐานะสมาชิกวัตถุแนะนำให้พวกเขาเป็นสถานะที่ไม่จำเป็นและปล่อยให้การเชื่อมต่อเปิดนานกว่าที่จำเป็น แต่การกระทำผ่านการเชื่อมต่อหรือธุรกรรมเป็นพารามิเตอร์นั้นไม่ผิดโดยกำเนิด

BTW ทั้งนี้ขึ้นอยู่กับการสนับสนุนภาษาของคุณสำหรับฟังก์ชั่นชั้นหนึ่งคุณอาจใช้เวลาในรายการของการกระทำ foobar () ดังนั้นฟังก์ชั่นหนึ่งสามารถจัดการกับพีชคณิตการกระทำทั้งหมดได้ กำจัดการทำซ้ำของบล็อกควบคุมภายนอกสำหรับการเปลี่ยนแปลงแต่ละครั้ง


การทำเครื่องหมายคำตอบนี้เป็นคำตอบที่ทำให้ฉันมีความคิดเพิ่มเติมเกี่ยวกับสถานการณ์ว่า
ipohfly

6

มันเสียงเหมือนคุณหลังจากที่พึ่งพาการฉีด นั่นคือการเชื่อมต่อ pooled จะถูกสร้างขึ้นหนึ่งครั้งและฉีดเมื่อใดก็ตามที่มันต้องการ แน่นอนว่าการส่งผ่านการเชื่อมต่อผ่านพารามิเตอร์ method เป็นวิธีหนึ่งในการพึ่งพาการฉีด แต่คอนเทนเนอร์ IoC เช่น Guice, PicoContainer หรือ Spring เป็นอีกวิธีหนึ่ง (ปลอดภัย) ที่คุณสามารถทำได้

การใช้ DI หมายความว่าคุณสามารถสรุปตรรกะรอบ ๆ การสร้างการเปิดการใช้งานและการปิดการเชื่อมต่อได้อย่างเรียบร้อย - ห่างจากตรรกะทางธุรกิจหลักของคุณ

Spring JDBC et al เป็นตัวอย่างของการแสดงพฤติกรรมเช่นนี้ให้คุณ


Emm ไม่ได้ดูที่การฉีดพึ่งพา เพียงแค่พยายามค้นหาว่าโดยทั่วไปเป็นวิธีปฏิบัติที่ดีหรือไม่หากไม่มีวิธีที่ดีกว่าคือการจัดการการเชื่อมต่อฐานข้อมูล (DI เป็นวิธีหนึ่งในการทำสิ่งนั้น)
ipohfly

-1 การเชื่อมต่อเดียวไม่เหมาะกับระบบผู้ใช้หลายคน อาจดูเหมือนจะทำงานได้เนื่องจากปริมาณผู้ใช้ต่ำและการดำเนินการที่รวดเร็ว ด้วยการรวมกำไรจะเป็นการดีกว่าที่จะยกตัวอย่างวัตถุการเชื่อมต่อต่อการกระทำแม้ในระบบผู้ใช้เดียว
mike30

2

การส่งผ่านสิ่งต่างๆในฐานข้อมูลมากกว่าสิ่งต่าง ๆ สามารถนำไปสู่ปัญหาได้ ในระดับนั้นทุกครั้งที่ใช้งานจริงจะต้องไม่ส่งผ่านสิ่งใด ๆ ของฐานข้อมูลเว้นแต่จะสามารถรับประกันความถูกต้องของฐานข้อมูลได้

ปัญหาเกี่ยวกับการส่งผ่านสิ่งต่างๆในฐานข้อมูลก็คือมันอาจจะเลอะเทอะ ฉันเห็นข้อผิดพลาดในรหัสมากกว่าหนึ่งรายการโดยมีคนผ่านการเชื่อมต่อฐานข้อมูลว่ามีคนคว้าผลลัพธ์ที่ตั้งไว้และหยุดทำงานในวัตถุในท้องถิ่น (ชุดผลลัพธ์ยังเชื่อมต่อกับฐานข้อมูล) แล้วผูกเคอร์เซอร์ใน ฐานข้อมูลสำหรับเวลาที่สำคัญ อินสแตนซ์อื่นที่มีคนส่งชุดผลลัพธ์ไปให้บุคคลอื่น (ซึ่งถูกเก็บไว้แล้ว) และวิธีที่ผ่านชุดผลลัพธ์นั้นปิด (และคำสั่ง) ซึ่งนำไปสู่ข้อผิดพลาดเมื่อวิธีอื่นพยายามทำงานกับชุดผลลัพธ์ที่ไม่ได้อีกต่อไป

ทั้งหมดนี้เกิดจากการไม่เคารพฐานข้อมูลการเชื่อมต่อคำสั่งชุดผลลัพธ์และวงจรชีวิต

เพื่อหลีกเลี่ยงปัญหานี้มีรูปแบบและโครงสร้างที่เล่นกับฐานข้อมูลได้ดีกว่าและไม่มีสิ่งที่ฐานข้อมูลจำเป็นต้องออกจากคลาสที่พวกเขาถูกกักตัวไว้ข้อมูลจะเข้าสู่ข้อมูลออกไป


1
การเชื่อมต่อ +1 db ควรมีช่วงเวลาสั้นที่สุดเท่าที่จะเป็นไปได้ เปิดใช้มันปิดมันให้เร็วที่สุด ทุกวันนี้มีการใช้งานพูลการเชื่อมต่อมากมายดังนั้นการใช้การเชื่อมต่อสำหรับการดำเนินงานหลาย ๆ ครั้งจึงเป็นเรื่องเศรษฐกิจที่ผิดพลาด และได้รับเชิญสำหรับข้อบกพร่องหรือปัญหาประสิทธิภาพการทำงาน (ถือล็อคตารางการใช้ทรัพยากรการเชื่อมต่อ)
JQA

รูปแบบและโครงสร้างที่มีอยู่เหล่านี้มีชื่อว่าอะไรบ้าง?
Daniel Kaplan

@tieTYT ตัวหลักที่อยู่ในแถวหน้าคือData access objectที่ทำหน้าที่ซ่อนฐานข้อมูลจากแอปพลิเคชั่นที่เหลือ ดูที่ Data Access Layerและการทำแผนที่วัตถุสัมพันธ์

เมื่อฉันนึกถึงรูปแบบเหล่านั้นฉันรู้สึกว่าพวกเขาอยู่ในระดับที่เป็นนามธรรมสูงกว่าสิ่งที่เขาถาม สมมติว่าคุณมีวิธีที่จะได้รับRootจาก Dao แต่คุณก็รู้ว่าคุณต้องการวิธีที่จะได้รับ a Nodeโดยไม่ต้องดึงRootวัตถุทั้งหมดด้วย คุณทำให้RootDao เรียกNodeรหัส Dao ได้อย่างไร (เช่น: นำมาใช้ซ้ำ) แต่ให้แน่ใจว่าNodeDao ปิดการเชื่อมต่อเฉพาะเมื่อNodeDao ถูกเรียกโดยตรงและเปิดการเชื่อมต่อไว้เมื่อRootDao ถูกเรียก?
Daniel Kaplan

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ชนิดที่แน่นอนของวัตถุที่จะเป็น ในรหัสเก่าฉันเคยมี singleton จัดการแหล่งข้อมูลที่มีอยู่ทั้งหมดภายในแอปพลิเคชันของฉัน DAO ของฉันไม่จำเป็นต้องรู้เกี่ยวกับสิ่งนั้นเนื่องจากเป็นDataSourceตัวอย่างที่มีให้ผ่านการฉีดแบบพึ่งพา
KevinLH

หากคุณใช้สคีมานี้ให้ลองใช้กับแหล่งข้อมูลที่ดีกว่า
user1156544

ย้อนกลับไปเมื่อฉันตอบฉันยังไม่ได้ใช้ Java 7 แต่คุณถูกต้องว่านี่ควรเป็นวิธีที่ต้องการในวันนี้ ฉันจะอัปเดตคำตอบของฉันเพื่อรวมข้อเสนอแนะของคุณ
KevinLH

0

มีข้อเสียเปรียบในการทำสิ่งต่าง ๆ ด้วยวิธีนี้แทนที่จะใช้ซิงเกิลซึ่งคุณสามารถทำได้ตามต้องการ ฉันเคยทำสิ่งต่าง ๆ ทั้งสองวิธีในอดีต

โดยทั่วไปคุณต้องคิดเกี่ยวกับผลที่ตามมาของการจัดการการเชื่อมต่อฐานข้อมูลและสิ่งนี้อาจจะใช่หรือไม่ใช่ orthogonal ต่อการใช้การสืบค้นฐานข้อมูล ตัวอย่างเช่นหากคุณมีการเชื่อมต่อหนึ่งฐานข้อมูลสำหรับอินสแตนซ์ของแอปพลิเคชันที่กำหนดและจะถูกปิดเมื่อไม่ใช้งานนั่นจะเป็นมุมฉาก วางการจัดการในชั้นเรียนเดี่ยวและไม่ผ่านมันไป สิ่งนี้อนุญาตให้คุณจัดการการเชื่อมต่อ db ตามที่คุณต้องการ ตัวอย่างเช่นหากคุณต้องการปิดการเชื่อมต่อทุกการกระทำ (และเปิดอีกครั้งในการโทรครั้งถัดไป) การทำเช่นนี้จะทำได้ง่ายกว่าในซิงเกิลตันเนื่องจาก API สำหรับสิ่งนี้สามารถรวมศูนย์ได้

ในทางกลับกันสมมติว่าคุณต้องจัดการกลุ่มการเชื่อมต่อที่การโทรที่กำหนดอาจต้องใช้การเชื่อมต่อโดยพลการใด ๆ สิ่งนี้อาจเกิดขึ้นเมื่อทำธุรกรรมแบบกระจายข้ามหลายเซิร์ฟเวอร์ตัวอย่างเช่น ในกรณีนี้คุณมักจะดีกว่าผ่านการเชื่อมต่อวัตถุ db กว่าที่คุณทำงานกับ singletons ฉันคิดว่านี่เป็นกรณีที่หายาก แต่ไม่มีอะไรผิดปกติกับการทำเมื่อคุณต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.