Je suis venu avec une application multi-locataire dynamique de travail en utilisant:dynamique multi-locataires WebApp (Spring Hibernate)
- Java 8
- Java Servlet 3.1
- Spring 3.0.7-RELEASE (ne peut pas changer la version)
- Mise en veille prolongée 3.6.0.Final (ne peut pas changer la version)
- Commons dbcp2
C'est la 1ère fois que je devais me instancier Spring objets Je me demande donc si je l'ai tout fait correctement ou si l'application va exploser dans mon visage à une date future non spécifiée pendant la production.
Fondamentalement, le schéma DataBase unique est connu, mais les détails de la base de données seront spécifiés lors de l'exécution par l'utilisateur. Ils sont libres de spécifier n'importe quel nom d'hôte/port/nom de DB/nom d'utilisateur/mot de passe.
est ici le flux de travail:
- L'utilisateur se connecte à l'application Web alors soit choisit une base de données à partir d'une liste connue, ou spécifie une base de données personnalisée (nom d'hôte/port/etc.).
- Si le Hibernate
SessionFactory
est construit avec succès (ou est trouvé dans le cache), alors il est persistant pour la session de l'utilisateur en utilisantSourceContext#setSourceId(SourceId)
puis l'utilisateur peut travailler avec cette base de données. - Si quelqu'un choisit/spécifie la même base de données, le même mises en cache
AnnotationSessionFactoryBean
est retourné - L'utilisateur peut changer les bases de données à tout moment.
- Lorsque l'utilisateur passe loin d'une commande DB (ou se déconnecte), les mises en cache
AnnotationSessionFactoryBean
s sont supprimés/détruits
Ainsi seront les travaux suivants comme prévu? L'aide et les pointeurs sont les bienvenus.
web.xml
<web-app version="3.1" ...>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener> <!-- Needed for SourceContext -->
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<web-app>
applicationContext.xml
<beans ...>
<tx:annotation-driven />
<util:properties id="db" location="classpath:db.properties" /> <!-- driver/url prefix -->
<context:component-scan base-package="com.example.basepackage" />
</beans>
UserDao.java
@Service
public class UserDao implements UserDaoImpl {
@Autowired
private TemplateFactory templateFactory;
@Override
public void addTask() {
final HibernateTemplate template = templateFactory.getHibernateTemplate();
final User user = (User) DataAccessUtils.uniqueResult(
template.find("select distinct u from User u left join fetch u.tasks where u.id = ?", 1)
);
final Task task = new Task("Do something");
user.getTasks().add(task);
TransactionTemplate txTemplate = templateFactory.getTxTemplate(template);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
template.save(task);
template.update(user);
}
});
}
}
TemplateFactory.java
@Service
public class TemplateFactory {
@Autowired
private SourceSessionFactory factory;
@Resource(name = "SourceContext")
private SourceContext srcCtx; // session scope, proxied bean
@Override
public HibernateTemplate getHibernateTemplate() {
LocalSessionFactoryBean sessionFactory = factory.getSessionFactory(srcCtx.getSourceId());
return new HibernateTemplate(sessionFactory.getObject());
}
@Override
public TransactionTemplate getTxTemplate(HibernateTemplate template) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(template.getSessionFactory());
return new TransactionTemplate(txManager);
}
}
SourceContext.java
@Component("SourceContext")
@Scope(value="session", proxyMode = ScopedProxyMode.INTERFACES)
public class SourceContext {
private static final long serialVersionUID = -124875L;
private SourceId id;
@Override
public SourceId getSourceId() {
return id;
}
@Override
public void setSourceId(SourceId id) {
this.id = id;
}
}
SourceId.java
public interface SourceId {
String getHostname();
int getPort();
String getSID();
String getUsername();
String getPassword();
// concrete class has proper hashCode/equals/toString methods
// which use all of the SourceIds properties above
}
SourceSessionFactory.java
@Service
public class SourceSessionFactory {
private static Map<SourceId, AnnotationSessionFactoryBean> cache = new HashMap<SourceId, AnnotationSessionFactoryBean>();
@Resource(name = "db")
private Properties db;
@Override
public LocalSessionFactoryBean getSessionFactory(SourceId id) {
synchronized (cache) {
AnnotationSessionFactoryBean sessionFactory = cache.get(id);
if (sessionFactory == null) {
return createSessionFactory(id);
}
else {
return sessionFactory;
}
}
}
private AnnotationSessionFactoryBean createSessionFactory(SourceId id) {
AnnotationSessionFactoryBean sessionFactory = new AnnotationSessionFactoryBean();
sessionFactory.setDataSource(new CutomDataSource(id, db));
sessionFactory.setPackagesToScan(new String[] { "com.example.basepackage" });
try {
sessionFactory.afterPropertiesSet();
}
catch (Exception e) {
throw new SourceException("Unable to build SessionFactory for:" + id, e);
}
cache.put(id, sessionFactory);
return sessionFactory;
}
public void destroy(SourceId id) {
synchronized (cache) {
AnnotationSessionFactoryBean sessionFactory = cache.remove(id);
if (sessionFactory != null) {
if (LOG.isInfoEnabled()) {
LOG.info("Releasing SessionFactory for: " + id);
}
try {
sessionFactory.destroy();
}
catch (HibernateException e) {
LOG.error("Unable to destroy SessionFactory for: " + id);
e.printStackTrace(System.err);
}
}
}
}
}
CustomDataSource.java
public class CutomDataSource extends BasicDataSource { // commons-dbcp2
public CutomDataSource(SourceId id, Properties db) {
setDriverClassName(db.getProperty("driverClassName"));
setUrl(db.getProperty("url") + id.getHostname() + ":" + id.getPort() + ":" + id.getSID());
setUsername(id.getUsername());
setPassword(id.getPassword());
}
}