Comment configurer les datasources pour un accès en lecture uniquement?

commons-logo

Ceci est un "tip" vraiment utile mais qui n'est peut-être pas suffisamment connu. Je vais vous expliquer dans cet article comment configurer les datasources dans un projet pour garantir un accès uniquement en lecture.

Un bon moyen d'optimiser et de mieux configurer  le pool de datasource est de fixer la property defaultReadOnly="true" lorsque cela est nécessaire. On peut même imaginer, si le contexte s'y prête, de configurer plusieurs datasources dans le projet dont certaines réservées uniquement à la lecture.

Faisant ainsi, on garantit que l'accès, via le pool datasource configuré, se fait uniquement en lecture. Bien que tous les moteurs de base de données ne supportent pas cet attribut ( ex. Informix).

Sur la démo ci-dessous, le pool est configuré en settant defaultReadOnly="true" pour postgres. Avec ce pool, on est sûr que le référentiel accédé ne sera jamais accidentellement modifié.

Et le test JUnit permet de valider qu'une opération CRUD via ce pool est rejetée.

ETAPE 1. Configurer la property defaultReadOnly

Selon le projet, la datasource est généralement configurée dans context.xml pour le serveur tomcat ou encore dans un fichier xml à part. L'exemple suivant est un extrait d'un fichier context.xml situé dans META-INF pour le projet web.

<Resource name="${postgres.database.datasource}"
			auth="Container"
			factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory" 
			type="javax.sql.DataSource"
			
                        defaultReadOnly="true"
			
                        initialSize="1" 
			maxActive="25" 
			 minIdle="2"
			maxIdle="15"
			maxWait="-1"
                        validationQuery="SELECT 1 "  
			username="${postgres.database.username}"
			password="${postgres.database.password}"
			driverClassName="${postgres.database.driver}"
			url="${postgres.database.url}"
/>

La seule chose intéressante est la property qui ne concerne defaultReadOnly. Notez également l'utilisation de BasicDataSourceFactory de l'api DBCP.

Attention, voici ce que dit la documentation officielle (pool tomcat & dbcp): "The default read-only state of connections created by this pool.If not set, the setReadOnly method will not be called. (Some drivers do not support read only mode...)"

ETAPE 2. Test JUnit de vérification

A cette étape on illustre juste que ce paramètre est pris en compte dans notre context : Postgres et l'api DBCP.

Pour mon test avec Spring configuré ainsi:

<bean id="ds" 
	class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
		p:driverClassName="${postgres.database.driver}" 
		p:url="${postgres.database.url}" 
		p:username="${postgres.database.username}" 
		p:password="${postgres.database.password}"
		p:defaultReadOnly="true"
		p:defaultAutoCommit="true"
		/>

Et le test JUnit doit contenir essentiellement ce morceau de code (à compléter voir annexe1):

Notez que j'utilise le fameux jdbcTemplate de Spring avec

 @ExpectedException(UncategorizedSQLException.class)
 @Test public void testCreateMustThrowException){
           final String sql="CREATE TABLE TEMPO11 (id char(3) )";
	   pgService.getJdbcTemplate().execute(sql);
}

 

Le résultat de ce test unitaire est bien vert et s'explique par la présence de l'annotation @ExpectedException.

Si on commente cette annotation puis on relance le test on obtient ces lignes:

UncategorizedSQLException: StatementCallback; uncategorized SQLException for SQL ...; 
SQL state [25006]; error code [0]; 
ERREUR: ne peut pas exécuter CREATE TABLE dans une transaction en lecture seule; 

 

C'est bien explicite que la datasource étant en readOnly, impossible de créer une table!

Je vous  laisse imaginer tout l'intérêt en gain de performance puisque ces transactions sont en lecture seule.

A utiliser sans modération si le contexte le permet.

ANNEXE 1

Le test JUnit bien évidemment repose sur le service PgService qui doit déclarer la ligne suivante avec Spring:

@Autowired
	@Qualifier("ds")
	public void setDataSource(DataSource dataSource) {
	  jdbcTemplate = new JdbcTemplate(dataSource);
	}

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.