MongoTemplate ของ Spring Data กับ MongoRepository ต่างกันอย่างไร


102

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

ฉันกำลังพูดถึงคำถามเช่นนี้:

@Repository
public interface UserRepositoryInterface extends MongoRepository<User, String> {
    List<User> findByEmailOrLastName(String email, String lastName);
}

หรือการใช้แบบสอบถามที่ใช้ JSON ซึ่งฉันลองโดยการลองผิดลองถูกเพราะฉันไม่เข้าใจไวยากรณ์ที่ถูกต้อง แม้หลังจากอ่านเอกสาร mongodb แล้ว (ตัวอย่างที่ใช้งานไม่ได้เนื่องจากไวยากรณ์ไม่ถูกต้อง)

@Repository
public interface UserRepositoryInterface extends MongoRepository<User, String> {
    @Query("'$or':[{'firstName':{'$regex':?0,'$options':'i'}},{'lastName':{'$regex':?0,'$options':'i'}}]")
    List<User> findByEmailOrFirstnameOrLastnameLike(String searchText);
} 

หลังจากอ่านเอกสารทั้งหมดแล้วดูเหมือนว่าmongoTemplateจะดีกว่าMongoRepositoryมาก ฉันอ้างถึงเอกสารดังต่อไปนี้:

http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/

ช่วยบอกหน่อยได้ไหมว่าอะไรใช้สะดวกและทรงพลังกว่ากัน mongoTemplateหรือMongoRepository? ทั้งคู่เป็นผู้ใหญ่เหมือนกันหรือคนใดคนหนึ่งขาดคุณสมบัติมากกว่ากัน?

คำตอบ:


136

"สะดวก" และ "ใช้งานได้อย่างมีประสิทธิภาพ" นั้นขัดแย้งกับเป้าหมายในระดับหนึ่ง ที่เก็บนั้นสะดวกกว่าเทมเพลตมาก แต่อย่างหลังแน่นอนว่าให้คุณควบคุมสิ่งที่ต้องดำเนินการได้อย่างละเอียดมากขึ้น

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

TL; ดร

โดยทั่วไปเราแนะนำแนวทางต่อไปนี้:

  1. เริ่มต้นด้วยบทคัดย่อของที่เก็บและเพียงแค่ประกาศการสืบค้นแบบธรรมดาโดยใช้กลไกการสร้างแบบสอบถามหรือการสืบค้นที่กำหนดเอง
  2. สำหรับการสืบค้นที่ซับซ้อนมากขึ้นให้เพิ่มวิธีการใช้งานด้วยตนเองลงในที่เก็บ (ตามเอกสารที่นี่) MongoTemplateสำหรับการใช้งานการดำเนินการ

รายละเอียด

สำหรับตัวอย่างของคุณสิ่งนี้จะมีลักษณะดังนี้:

  1. กำหนดอินเทอร์เฟซสำหรับโค้ดที่คุณกำหนดเอง:

    interface CustomUserRepository {
    
      List<User> yourCustomMethod();
    }
    
  2. เพิ่มการใช้งานสำหรับคลาสนี้และทำตามหลักการตั้งชื่อเพื่อให้แน่ใจว่าเราสามารถค้นหาคลาสได้

    class UserRepositoryImpl implements CustomUserRepository {
    
      private final MongoOperations operations;
    
      @Autowired
      public UserRepositoryImpl(MongoOperations operations) {
    
        Assert.notNull(operations, "MongoOperations must not be null!");
        this.operations = operations;
      }
    
      public List<User> yourCustomMethod() {
        // custom implementation here
      }
    }
    
  3. ตอนนี้ให้อินเทอร์เฟซที่เก็บฐานของคุณขยายส่วนที่กำหนดเองและโครงสร้างพื้นฐานจะใช้การใช้งานที่กำหนดเองของคุณโดยอัตโนมัติ:

    interface UserRepository extends CrudRepository<User, Long>, CustomUserRepository {
    
    }
    

วิธีนี้คุณเป็นหลักได้รับเลือก: ทุกอย่างที่เป็นเพียงแค่เรื่องง่ายที่จะประกาศจะเข้าสู่ทุกอย่างที่ดำเนินการด้วยตนเองดีกว่าจะเข้าสู่UserRepository CustomUserRepositoryตัวเลือกการปรับแต่งที่มีเอกสารที่นี่


1
สวัสดีโอลิเวอร์สิ่งนี้ใช้ไม่ได้จริง spring-data พยายามสร้างคำค้นหาโดยอัตโนมัติจากชื่อที่กำหนดเอง yourCustomMethod () มันจะบอกว่า "ของคุณ" ไม่ใช่ฟิลด์ที่ถูกต้องในคลาสโดเมน ฉันทำตามคู่มือและตรวจสอบอีกครั้งว่าคุณกำลังทำอะไรบ้างในตัวอย่าง spring-data-jpa ไม่มีโชค. spring-data พยายามสร้างอัตโนมัติเสมอทันทีที่ฉันขยายอินเทอร์เฟซที่กำหนดเองไปยังคลาสที่เก็บ ข้อแตกต่างเพียงอย่างเดียวคือตอนนี้ฉันใช้ MongoRepository ไม่ใช่ CrudRepository เนื่องจากฉันไม่ต้องการทำงานกับ Iterators ในตอนนี้ หากคุณมีคำใบ้จะได้รับการชื่นชม
Christopher Armstrong

11
ความผิดพลาดที่พบบ่อยที่สุดคือการตั้งชื่อที่ไม่ถูกต้องระดับการดำเนินงาน: ถ้าอินเตอร์เฟซฐาน repo ของคุณเรียกว่าระดับการดำเนินการจะต้องมีการตั้งชื่อYourRepository YourRepositoryImplเป็นอย่างนั้นหรือ? ถ้าเป็นเช่นนั้นฉันยินดีที่จะดูโครงการตัวอย่างใน GitHub หรืออื่น ๆ ...
Oliver Drotbohm

5
สวัสดีโอลิเวอร์คลาส Impl ถูกตั้งชื่อผิดตามที่คุณคาดเดา ฉันปรับชื่อและดูเหมือนว่ามันใช้งานได้แล้ว ขอบคุณมากสำหรับความคิดเห็นของคุณ มันเจ๋งมากที่สามารถใช้ตัวเลือกการสืบค้นประเภทต่างๆด้วยวิธีนี้ ผ่านความคิดมาแล้ว!
Christopher Armstrong

คำตอบนี้ไม่ชัดเจนนัก หลังจากทำทุกอย่างตามตัวอย่างนี้ผมตกอยู่กับปัญหานี้: stackoverflow.com/a/13947263/449553 หลักการตั้งชื่อจึงเข้มงวดกว่าที่คิดจากตัวอย่างนี้
msangel

1
ชั้นการดำเนินงานใน # 2 เป็นชื่อที่ไม่ถูกต้อง: ควรจะเป็นและไม่ได้CustomUserRepository CustomerUserRepository
Cotta

27

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

ให้ไปตามMongoTemplateเส้นทางและสร้างชั้นการเข้าถึงข้อมูลของคุณเองซึ่งจะช่วยปลดปล่อยคุณจากฝันร้ายของการกำหนดค่าที่โปรแกรมเมอร์ Spring ต้องเผชิญ MongoTemplateเป็นผู้ช่วยให้วิศวกรที่สะดวกสบายในการออกแบบชั้นเรียนและการโต้ตอบของตนเองเนื่องจากมีความยืดหยุ่นมาก โครงสร้างอาจเป็นดังนี้:

  1. สร้างMongoClientFactoryคลาสที่จะทำงานในระดับแอปพลิเคชันและมอบMongoClientวัตถุให้คุณ คุณสามารถใช้สิ่งนี้เป็น Singleton หรือใช้ Enum Singleton (นี่คือเธรดที่ปลอดภัย)
  2. สร้างคลาสฐานการเข้าถึงข้อมูลซึ่งคุณสามารถสืบทอดอ็อบเจ็กต์การเข้าถึงข้อมูลสำหรับอ็อบเจ็กต์โดเมนแต่ละรายการ) คลาสพื้นฐานสามารถใช้เมธอดในการสร้างอ็อบเจ็กต์ MongoTemplate ซึ่งคลาสเมธอดเฉพาะของคุณสามารถใช้สำหรับการเข้าถึง DB ทั้งหมด
  3. คลาสการเข้าถึงข้อมูลแต่ละคลาสสำหรับอ็อบเจ็กต์โดเมนแต่ละตัวสามารถใช้วิธีการพื้นฐานหรือคุณสามารถนำไปใช้ในคลาสพื้นฐาน
  4. จากนั้นเมธอด Controller สามารถเรียกใช้เมธอดในคลาสการเข้าถึงข้อมูลได้ตามต้องการ

สวัสดี @rameshpa ฉันสามารถใช้ทั้ง MongoTemplate & repository ในโปรเจ็กต์เดียวกันได้หรือไม่ .. ใช้ได้ไหม
Gauranga

1
คุณสามารถทำได้ แต่ MongoTemplate ที่คุณใช้จะมีการเชื่อมต่อกับ DB ที่แตกต่างจากการเชื่อมต่อที่ใช้โดย Repository ปรมาณูอาจเป็นปัญหา ฉันไม่แนะนำให้ใช้การเชื่อมต่อที่แตกต่างกันสองแบบในเธรดเดียวหากคุณมีความต้องการในการจัดลำดับ
rameshpa

27

FWIW เกี่ยวกับการอัปเดตในสภาพแวดล้อมแบบมัลติเธรด:

  • MongoTemplateให้"อะตอม" ออกจากกล่องการดำเนินงาน updateFirst , updateMulti, findAndModify, upsert... ซึ่งช่วยให้คุณสามารถปรับเปลี่ยนเอกสารในการทำงานครั้งเดียว Updateวัตถุโดยใช้วิธีการเหล่านี้ยังช่วยให้คุณสามารถกำหนดเป้าหมายเฉพาะสาขาที่เกี่ยวข้อง
  • MongoRepositoryเพียง แต่ช่วยให้คุณมีการดำเนินงานพื้นฐาน CRUD find , insert, save, deleteซึ่งทำงานร่วมกับ POJOs ที่มีเขตข้อมูลทั้งหมด กองกำลังนี้คุณจะปรับปรุงทั้งเอกสารในหลายขั้นตอน ( 1. findเอกสารที่จะปรับปรุง 2. ปรับเปลี่ยนสาขาที่เกี่ยวข้องจาก POJO กลับมาแล้ว 3. saveมัน) @Queryหรือกำหนดคำสั่งปรับปรุงของคุณเองด้วยมือโดยใช้

ในสภาพแวดล้อมแบบมัลติเธรดเช่น Java back-end ที่มีจุดสิ้นสุด REST หลายจุดการอัปเดตวิธีเดียวเป็นวิธีที่จะดำเนินการเพื่อลดโอกาสที่การอัปเดตพร้อมกันสองรายการจะเขียนทับการเปลี่ยนแปลงของกันและกัน

ตัวอย่าง: ได้รับเอกสารเช่นนี้{ _id: "ID1", field1: "a string", field2: 10.0 }และสองเธรดที่แตกต่างกันพร้อมกันอัปเดต ...

ด้วยMongoTemplateก็จะมีลักษณะค่อนข้างเช่นนี้

THREAD_001                                                      THREAD_002
|                                                               |
|update(query("ID1"), Update().set("field1", "another string")) |update(query("ID1"), Update().inc("field2", 5))
|                                                               |
|                                                               |

และสถานะสุดท้ายสำหรับเอกสารจะอยู่เสมอ{ _id: "ID1", field1: "another string", field2: 15.0 }เนื่องจากแต่ละเธรดเข้าถึง DB เพียงครั้งเดียวและมีการเปลี่ยนแปลงเฉพาะฟิลด์ที่ระบุเท่านั้น

ในขณะที่สถานการณ์กรณีเดียวกันMongoRepositoryจะมีลักษณะดังนี้:

THREAD_001                                                      THREAD_002
|                                                               |
|pojo = findById("ID1")                                         |pojo = findById("ID1")
|pojo.setField1("another string") /* field2 still 10.0 */       |pojo.setField2(pojo.getField2()+5) /* field1 still "a string" */
|save(pojo)                                                     |save(pojo)
|                                                               |
|                                                               |

และเอกสารสุดท้ายเป็นอย่างใดอย่างหนึ่ง{ _id: "ID1", field1: "another string", field2: 10.0 }หรือ{ _id: "ID1", field1: "a string", field2: 15.0 }ขึ้นอยู่กับว่าsaveการดำเนินการใดถึง DB ล่าสุด
(หมายเหตุ: แม้ว่าเราจะใช้คำอธิบายประกอบของ Spring Data@Versionตามที่แนะนำในความคิดเห็น แต่ก็ไม่มีอะไรเปลี่ยนแปลงมากนัก: การsaveดำเนินการอย่างใดอย่างหนึ่งจะส่งผลOptimisticLockingFailureExceptionกระทบและเอกสารสุดท้ายจะยังคงเป็นหนึ่งในข้อข้างต้นโดยมีการอัปเดตเพียงช่องเดียวแทนที่จะเป็นทั้งสองอย่าง )

ดังนั้นฉันจะบอกว่านั่นMongoTemplateเป็นตัวเลือกที่ดีกว่าเว้นแต่คุณจะมีโมเดล POJO ที่ละเอียดมากหรือต้องการความสามารถในการสืบค้นแบบกำหนดเองMongoRepositoryด้วยเหตุผลบางประการ


ประเด็น / ตัวอย่างที่ดี อย่างไรก็ตามตัวอย่างสภาพการแข่งขันและผลลัพธ์ที่ไม่ต้องการของคุณสามารถหลีกเลี่ยงได้โดยใช้ @Version เพื่อป้องกันสถานการณ์นั้น ๆ
Madbreaks

@Madbreaks คุณสามารถจัดหาแหล่งข้อมูลเกี่ยวกับวิธีการบรรลุเป้าหมายนี้ได้หรือไม่? เอกสารอย่างเป็นทางการใด ๆ อาจ?
Karthikeyan


1
@Madbreaks ขอบคุณที่ชี้ให้เห็น ใช่@Versionจะ "หลีกเลี่ยง" เธรดที่สองเขียนทับข้อมูลที่บันทึกไว้โดยเธรดแรก - "หลีกเลี่ยง" ในแง่ที่ว่ามันจะทิ้งการอัปเดตและโยนOptimisticLockingFailureExceptionแทน ดังนั้นคุณจะต้องใช้กลไกการลองใหม่หากคุณต้องการให้การอัปเดตสำเร็จ MongoTemplate ช่วยให้คุณหลีกเลี่ยงสถานการณ์ทั้งหมด
walen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.