2016-04-06 5 views
5

मेरे पास एक ऐसी प्रणाली है जहां किरायेदारों की एक अज्ञात संख्या है (उसी डेटाबेस सर्वर पर विभिन्न डेटाबेस उदाहरण)। मेरे पास वर्किंग कोड है जहां कोई उपयोगकर्ता लॉग इन करता है और सही किरायेदार का चयन किया जाता है, और मैं उस किरायेदार के लिए कॉन्फ़िगरेशन टेबल पढ़ सकता हूं।एकाधिक किरायेदारों (समान तालिका नाम) में क्वेरी टेबल

मैं चाहता हूं कि आवेदन सभी किरायेदारों के माध्यम से लूप करने के लिए प्रारंभ समय पर हो, कॉन्फ़िगरेशन पढ़ें और उस पर कार्य करें। स्प्रिंग डेटा जेपीए (हाइबरनेट द्वारा समर्थित) में जाने से पहले यह आसान था क्योंकि मैं प्रत्येक डेटाबेस इंस्टेंस से अलग से कनेक्ट कर रहा था।

मुझे नहीं लगता कि मैं वसंत के @Transactional का उपयोग कर सकता हूं क्योंकि यह केवल एक कनेक्शन स्थापित करता है।

मुझे उसी बीन के साथ एक ही भंडार इंटरफेस का उपयोग करने की उम्मीद है, क्योंकि यह काम करता है जब मुझे केवल एक किरायेदार को एक समय में मारने की आवश्यकता होती है।

मेरे पास class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl है जो मुझे किसी दिए गए किरायेदार के लिए डेटा स्रोत देगा, लेकिन मुझे यकीन नहीं है कि @Service कक्षा की विधि में इसका उपयोग कैसे किया जाए?

+0

जब आप कहते हैं कि "कॉन्फ़िगरेशन पढ़ें" क्या आपका मतलब प्रत्येक किरायेदार के डेटाबेस में एक तालिका है? आप इस ब्लॉग [पोस्ट] (http://anakiou.blogspot.my/2015/08/multi-tenant-plplication-with-spring.html) पर एक नज़र डालना चाहेंगे जो आपको कुछ विचार दे सकता है। यदि आपके पास डेटा स्रोतों की एक सूची है, तो मुझे लगता है कि आपका एप्लिकेशन शुरू होने पर उन्हें लूप करना मुश्किल नहीं होगा। उदाहरण के रूप में "कॉन्फ़िगरेशन टेबल" का उपयोग करके – Mustafa

+0

खराब विकल्प हो सकता है, क्योंकि यह चीजों को भ्रमित कर सकता है। मेरे पास वास्तव में कुछ चीजें हैं जिन्हें विभिन्न किरायेदारों के लिए विभिन्न कारणों से पढ़ना होगा। आपके द्वारा प्रदान किया गया ब्लॉग पोस्ट बताता है कि डेटा स्रोतों को आगे बढ़ाना, जो कुछ मुझे नहीं पता। –

+0

बहु-किरायेदार द्वारा क्या आपका मतलब है 'उपयोगकर्ता' नामक एक टेबल होने जैसा कुछ डेटाबेस डेटाबेस पर 'dropbox_db',' google_drive_db', 'sky_drive_db' आदि जैसे कई डेटाबेस में दिखाई देता है? – Saheed

उत्तर

2

मुझे यकीन नहीं है कि मुझे अपना पिछला उत्तर हटा देना चाहिए, ई इसे या क्या करें। तो यदि कोई एमओडी मुझे उचित प्रक्रिया बता सकता है तो मुझे अनुपालन करने में खुशी होगी।

बाहर निकलता है मैं @Transactional के उपयोग के बारे में सही नहीं था। मैं अपने MultiTenantConnectionProviderImpl और CurrentTenantResolverImpl को प्रतिस्थापित करने के लिए एक कस्टम कार्यान्वयन और AbstractRoutingDataSource का उपयोग करके समाप्त हुआ।मैं इस नए डेटा स्रोत के बजाय का उपयोग hibernate.multiTenancyhibernate.multi_tenant_connection_provider और hibernate.tenant_identifier_resolver

मेरे अस्थायी ओवरराइड वर्ग की स्थापना के इस तरह दिखता है:

public class MultitenancyTemporaryOverride implements AutoCloseable 
{  
    static final ThreadLocal<String> tenantOverride = new NamedThreadLocal<>("temporaryTenantOverride"); 

    public void setCurrentTenant(String tenantId) 
    { 
     tenantOverride.set(tenantId); 
    } 

    public String getCurrentTenant() 
    { 
     return tenantOverride.get(); 
    } 

    @Override 
    public void close() throws Exception 
    { 
     tenantOverride.remove(); 
    } 
} 

मेरे TenantRoutingDataSource इस तरह दिखता है:

@Component 
public class TenantRoutingDataSource extends AbstractDataSource implements InitializingBean 
{ 

    @Override 
    public Connection getConnection() throws SQLException 
    { 
     return determineTargetDataSource().getConnection(); 
    } 

    @Override 
    public Connection getConnection(String username, String password) throws SQLException 
    { 
     return determineTargetDataSource().getConnection(username, password); 
    } 

    @Override 
    public void afterPropertiesSet() throws Exception 
    { 
    } 

    protected String determineCurrentLookupKey() 
    { 
     Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
     String database = "shared"; 
     if (authentication != null && authentication.getPrincipal() instanceof MyUser) 
     { 
      MyUser user = (MyUser) authentication.getPrincipal(); 
      database = user.getTenantId(); 
     } 
     String temporaryOverride = MultitenancyTemporaryOverride.tenantOverride.get(); 
     if (temporaryOverride != null) 
     { 
      database = temporaryOverride; 
     } 
     return database; 
    } 

    protected DataSource determineTargetDataSource() 
    { 
     return selectDataSource(determineCurrentLookupKey()); 
    } 

    public DataSource selectDataSource(String tenantIdentifier) 
    { 
     //I use C3P0 for my connection pool 
     PooledDataSource pds = C3P0Registry.pooledDataSourceByName(tenantIdentifier); 
     if (pds == null) 
      pds = getComboPooledDataSource(tenantIdentifier); 
     return pds; 
    } 

    private ComboPooledDataSource getComboPooledDataSource(String tenantIdentifier) 
    { 
     ComboPooledDataSource cpds = new ComboPooledDataSource(tenantIdentifier); 
     cpds.setJdbcUrl("A JDBC STRING HERE"); 
     cpds.setUser("MyDbUsername"); 
     cpds.setPassword("MyDbPassword"); 
     cpds.setInitialPoolSize(10); 
     cpds.setMaxConnectionAge(10000); 
     try 
     { 
      cpds.setDriverClass("com.informix.jdbc.IfxDriver"); 
     } 
     catch (PropertyVetoException e) 
     { 
      throw new RuntimeException("Weird error when setting the driver class", e); 
     } 
     return cpds; 
    } 
} 

तब मैं सिर्फ प्रदान इसे बनाने के दौरान मेरे एंटिटी मैनेजर फैक्ट्री बीन के लिए मेरा कस्टम डेटा स्रोत।

@Service 
public class TestService 
{ 
    public void doSomeGets() 
    { 
     List<String> tenants = getListSomehow(); 
     try(MultitenancyTemporaryOverride tempOverride = new MultitenancyTemporaryOverride()) 
     { 
      for(String tenant : tenants) 
      { 
       tempOverride.setCurrentTenant(tenant); 
       //do some work here, which only applies to the tenant 
      } 
     } 
     catch (Exception e) 
     { 
      logger.error(e); 
     } 
    } 
} 
0

मुझे लगता है कि मैं एक समाधान के करीब हूं, लेकिन मैं इसके साथ पूरी तरह से खुश नहीं हूं। मैं आने के लिए एक बेहतर जवाब के लिए प्यार करता हूँ।

संपादित: पता चला है यह, नहीं काफी काम करता है वसंत के रूप में या हाइबरनेट केवल वर्तमान किरायेदार पहचानकर्ता रिसोल्वर एक बार फोन करने के लिए, हर बार के लिए एक @Transactional विधि

यह CurrentTenantIdentifierResolver कार्यान्वयन में बदला जाता है कहा जाता है नहीं दिखाई देता है वर्तमान उपयोगकर्ता को (यदि यह सेट किया गया है) को न केवल अपने वर्तमान किरायेदार आईडी (इसे सेट करने के तरीके को समझने के लिए कार्यान्वयन करने के लिए) को देखने के लिए ... इसे एक थ्रेड स्थानीय चर को देखने की आवश्यकता है ताकि यह देखने के लिए कि ओवरराइड निर्धारित किया गया है।

इस दृष्टिकोण का उपयोग करके, मैं अस्थायी रूप से किरायेदार सेट कर सकता हूं ... निर्दिष्ट मेरे बहु किरायेदारी लेनदेन प्रबंधक के साथ एक सेवा विधि को कॉल करें और फिर डेटा प्राप्त करें।

मेरे टेस्ट सेवा:

@Service 
public class TestService 
{ 
    @Transactional(transactionManager = "sharedTxMgr") 
    public void doSomeGets() 
    { 
     List<String> tenants = getListSomehow(); 
     try(MultitenancyTemporaryOverride tempOverride = new MultitenancyTemporaryOverride()) 
     { 
      for(String tenant : tenants) 
      { 
       tempOverride.setCurrentTenant(tenant); 
       doTenantSpecificWork(); 
      } 
     } 
     catch (Exception e) 
     { 
      logger.error(e); 
     } 
    } 

    @Transactional(transactionManager = "tenantSpecificTxMgr") 
    public void doTenantSpecificWork() 
    { 
     //do some work here, which only applies to the tenant 
    } 
} 

मेरी कक्षा कि ThreadLocal की स्थापना लपेटता, AutoCloseable लागू करने में मदद करने के लिए सुनिश्चित चर बनाने

public class MultitenancyTemporaryOverride implements AutoCloseable 
{ 
    static final ThreadLocal<String> tenantOverride = new ThreadLocal<>(); 

    public void setCurrentTenant(String tenantId) 
    { 
     tenantOverride.set(tenantId); 
    } 

    public String getCurrentTenant() 
    { 
     return tenantOverride.get(); 
    } 

    @Override 
    public void close() throws Exception 
    { 
     tenantOverride.remove(); 
    } 

} 

मेरे किरायेदार समाधानकर्ता कार्यान्वयन कि धागा स्थानीय

का उपयोग करता है ऊपर साफ किया जाता है
public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver 
{ 

    @Override 
    public String resolveCurrentTenantIdentifier() 
    { 
     Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
     logger.debug(ToStringBuilder.reflectionToString(authentication)); 
     String database = "shared"; 
     if (authentication != null && authentication.getPrincipal() instanceof MyUser) 
     { 
      MyUser user = (MyUser) authentication.getPrincipal(); 
      database = user.getTenantId(); 
     } 
     String temporaryOverride = MultitenancyTemporaryOverride.tenantOverride.get(); 
     if(temporaryOverride != null) 
     { 
      database = temporaryOverride; 
     } 
     return database; 
    } 
संबंधित मुद्दे