Sur la route d'Oxiane digressions diverses

LeBlog OXiane

26 jan
2012

Subversion dans les nuages

De passage au siège Oxiane, petite discussion avec Jeff. Il voudrait rendre le référentiel subversion d’un projet accessible de l’extérieur mais impossible de trouver ne serait-ce qu’une demi-heure pour configurer tout le monde sur le réseau.

Jeff, tu as cinq minutes ? Sers-toi un café, je te monte un subversion accessible y compris depuis Krypton.


.

t0

Je me connecte sur le site de cloudbees.

Je commence par ajouter un nouvel utilisateur à mon compte pour ton développeur (Account > Manage Users > Add a user).

Il semble que la personne ne reçoive pas de mail quand elle est inscrite par un tiers (?!?). L’astuce est d’aller sur la page « Forgot your Password? » et d’entrer l’e-mail du nouvel inscrit. Comme ça cloudbees va lui envoyer un lien permettant de saisir un mot de passe et se loguer pour la première fois.

t + 2 min

Je crée un nouveau référentiel Subversion pour mon projet. Un clic sur Create new code Repository.

t + 2 min 30

Le référentiel n’attend plus que son premier commit.

Et comme il me reste encore la moitié des cinq minutes allouées, on va lancer l’importation de ton repository subversion existant. On suit la doc :

svnsync init https://svn-oxiane.forge.cloudbees.com/LeProjetDeJeff/ URL_du_repo_source
svnsync sync https://svn-oxiane.forge.cloudbees.com/LeProjetDeJeff/

Et c’est parti mon kiki !

t + 3 min

La discussion se poursuit pendant que les révisions défilent sur l’écran. On aborde le sujet de l’intégration continue du projet en question chez cloudbees …
la suite au prochain épisode.

Guillaume Rams

24 jan
2012

Soirée PAUG de Janvier 2012 : IceScream Sandwich & ASOP

Ice Scream Sandwich est le nom de code de la toute dernière version de Android, la version 4. C’est l’un des deux thèmes de la première conférence PAUG de la nouvelle année qui s’est déroulée dans les locaux de l’ECE de Paris. . La deuxième partie de la soirée était consacrée aux problématiques de la compilation des sources de Android à travers un retour d’expérience sur le projet ASOP (Android Open Source Project).

Ice Scream Sandwich

Dans la nouvelle version de Android on notera en premier lieu, du point de vue utilisateur, l’amélioration du Look’n Feel et les fonctionnalités du « Multitasking ». La bonne nouvelle pour les développeurs est l’unification de la plateforme qui permet de développer une seule application pour tous les terminaux Android. Pour marquer le coup avec le succès des réseaux sociaux, de nouvelles API – Social API – sont ajoutées pour faciliter le partage des ressources sur Facebook, Google ou tweeter ou l’accès au profil utilisateur. Dans l’API Contacts, tout comme dans l’API Calendar, une nouvelle table et des permissions supplémentaires sont apparues. L’intégration du NFC pour la communication directe entre terminaux et les récentes couches de l’ « Accessibility Features » (la possibilité de faire une description vocale des éléments de l’écran) vont élargir le champ d’utilisation de la plateforme.

Les habitués de Swing, qui ont très tôt croisé le fer avec le TableLayout, apprécieront l’arrivée du Layout passe partout qu’est le GridLayout pour combler les limites du LinearLayout (défaut d’alignement).

(Android Open Source Project)

Daniel Fages est expert en construction de ROM Android, c’est lui qui a présenté le deuxième thème de la soirée, ASOP. D’abord pourquoi compiler Android ? Pour l’adapter à un usage spécifique, pour trouver d’éventuelles failles de sécurité, ou par simple curiosité. Le matériel nécessaire n’est pas à la portée de tout le monde, 16 Go RAM pour une durée de compilation de 30 minutes. Très peu de développeurs poussent leur audace aussi loin. En plus de la difficulté de maitriser la programmation système avec C/C++ il y a l’obstacle de la nature de la licence Apache et l’absence de communication de Google sur les orientations futures de son OS mobile.

Pour revivre la soirée avec beaucoup plus de détails les slides et les vidéos sont disponibles en ligne.

Yakhya Dabo

ydabo

23 jan
2012

La combinaison de LoaderManager et MediaStore.Audio sous Android

Bonjour,

Je vais vous parler de deux sujets dans cet article :
- comment récupérer les informations concernant la musique stockée sur un téléphone android
- comment gérer un chargement en tâche de fond avec les Loader

Obtenir des informations sur vos fichiers musicaux :

Le framework android procède un ContentProvider qui fourni des informations sur la musique stockée sur l’appareil. Il est donc possible de l’interroger comme n’importe quel autre ContentProvider.
Vous trouverez toutes les constantes intéressantes dans les sous classes de MediaStore.Audio.
http://developer.android.com/reference/android/provider/MediaStore.Audio.html

Ce qui vous permettra d’avoir des informations sur les playlists, les genres musicaux, les albums, les artistes, etc…

Par exemple pour obtenir la liste de tous les artistes correspondant à votre musique. Il suffit de faire comme ceci :

public List<String> getArtistsNames(Activity act) {
	ArrayList<String> artists = new ArrayList<String>();

	String[] proj = { MediaStore.Audio.Artists.ARTIST };
	Cursor musicCursor = act.managedQuery(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,proj, null, null, null);

	if(musicCursor.moveToFirst()) {
		do {
			String artistName = musicCursor.getString(0);
			artists.add(artistName);

			if( EtLog.isEnabled()) {
				EtLog.d(TAG,String.format("Identified Artist name is [%s]",artistName));
			}
		}
		while(musicCursor.moveToNext());
	}

	return artists;
}

Utiliser le LoaderManager pour gérer la recherche :

Nous allons utiliser une méthode disponible depuis une Activity ou un Fragment pour gérer un chargement en tâche de fonds.
Cette API a été ajoutée à partir du SDK 11, c’est à dire la version 3.0 (HoneyComb). Mais heureusement elle est disponible aussi sur les versions antérieures grâce au package de compatibilité dont j’ai déjà fait mention dans un article précédant.

Il y a plusieurs avantages à utiliser l’ API sur les Loader :
- Elle gère les chargements des données en asynchrone
- Elle gère le rafraîchissement des données
- Elle gère automatiquement le cycle de vie de telle sorte que le Loader n’est pas détruit/reconstruit à chaque changement de configuration

Le dernier point ici (gestion du cycle de vie) est très intéressant, car cela nous affranchit d’une tâche assez périlleuse. Je parle de la gestion d’une tâche asynchrone dans une activité, sans que la tâche soit perturbée par le cycle de vie de l’activité.

La classe LoaderManager est accessible directement depuis une Activity ou un Fragment grâce à la méthode getLoaderManager().

On peut remarquer dans les classes filles de Loader, qu’il existe une implémentation CursorLoader. Elle permet donc d’interroger un ContentProvider de manière asynchrone.
Il est possible cependant de créer son propre Loader en héritant de AsyncTaskLoader.

Pour créer un Loader il faut que votre Activity implémente LoaderManager.LoaderCallbacks, vous aurez donc 3 méthodes à implémenter :

public Loader<C> onCreateLoader(int id, Bundle args) ;
public void onLoadFinished(Loader<C> loader, C data);
public void onLoaderReset(Loader<C> loader);

onCreateLoader() ne sera appelé que si un nouveau Loader a besoin d’être créé. Cela n’est pas le cas lors d’un changement d’orientation puisque le LoaderManager se charge de déconnecter et reconnecter le Loader de l’Activity lors d’un changement de configuration.
onCreateFinished() : permet de récupérer le résultat d’un chargement.
onLoaderReset() est appelé lorsque que l’on quitte l’activité.

La dernière chose à laquelle il faut penser, c’est d’appeler la méthode initLoader.

initLoader (int id, Bundle args, LoaderCallbacks<D> callback)

Cette méthode ce charge de la création du Loader si besoin.
Vous remarquerez qu’il est possible de passer un id à cette méthode. Celui-ci sert à identifier le loader, car le LoaderManager peut gérer plusieurs loader.
On récupère donc cet identifiant dans la callback onCreateLoader().

Conclusion :

Voici un exemple d’utilisation d’un CusorLoader qui affiche la liste des artistes du Téléphone avec le nombre de musique associé à cet artiste.

public class CursorLoaderListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor>{
	// Adapter utilisé pour afficher la liste
    SimpleCursorAdapter mAdapter;

    @Override public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // On configure un texte à afficher si il n'y a pas de data
        setEmptyText("No artits music!");

        // On crée un adapter vide (le 3eme paramètre qui doit recevoir un Cursor est null)
        mAdapter = new SimpleCursorAdapter(getActivity(),
                android.R.layout.simple_list_item_2, null,
                new String []{  MediaStore.Audio.Artists.ARTIST , MediaStore.Audio.Artists.NUMBER_OF_ALBUMS},
                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
        setListAdapter(mAdapter);

        // On affiche une progressbar en attendant
        setListShown(false);

        // Prépare le Loader: on se reconnect avec celui existant ou on en cré un nouveau et on le démarre
        getLoaderManager().initLoader(0, null, this);
    }

    // Ce sont sont les colonnes que l'on va recevoir
    static final String[] ARTIST_SUMMARY_PROJECTION = { MediaStore.Audio.Artists._ID, MediaStore.Audio.Artists.ARTIST , MediaStore.Audio.Artists.NUMBER_OF_ALBUMS};

    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    	//Ceci est applelé lorsqu'on d'un nouveau Loader a besoin d'être créé.
    	//Cet exemple a besoin d'un seul Loader, donc nous ne nous occupons pas de l'ID.
    	//l'ID correspond à celui envoyé dans le 1er argument de la méthode getLoaderManager().initLoader(...)

        String select = null;
        return new CursorLoader(getActivity(), MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
        		ARTIST_SUMMARY_PROJECTION, select, null,
                null);
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    	//On change le Loader associé à l'adapter (Le framework s'occupe de fermer l'ancien Cursor)
        mAdapter.swapCursor(data);

        // On peut afficher la liste maintenant
        if (isResumed()) {
            setListShown(true);
        } else {
            setListShownNoAnimation(true);
        }
    }

    public void onLoaderReset(Loader<Cursor> loader) {

        mAdapter.swapCursor(null);
    }
shocq

shocq

19 jan
2012

Sauvegarder simplement en base de données avec ORMLite sous Android

Bonjour,

Je voudrais vous parler d’une bibliothèque très pratique pour sauvegarder des données dans la base SQLite d’un téléphone Android. Cette bibliothèque s’appelle ORMLite.

Je vous ai déjà parlé d’une autre bibliothèque pratique pour faire un parsing JSON : JacksonPaser.
Les deux bibliothèques peuvent s’associer parfaitement, ce qui offre un bon socle pour commencer un projet Android.

Ormlite supporte les connections JDBC à MySQL, Postgres, H2, SQLite, Derby,
HSQLDB, Microsoft SQL Server.
Mais ce qui nous intéresse ici c’est qu’ORMLite supporte les appels natifs à la base de données SQLite sous Android.

Si vous avez déjà essayé de faire de la sauvegarde en base sous android, vous vous êtes sûrement aperçu combien c’est fastidieux.
Personnellement je ne trouve pas cela très pratique. Je préfère utiliser un ORM (Object Relational Mapping) qui fait un mapping entre mes objets métier et les enregistrements en base de données.

Certes on peut avoir des dégradations de performances, mais au vu du confort apporté au niveau de la programmation, le choix est vite fait.

Installation

Il faudra télécharger 2 fichiers jar, les déposer dans un répertoire lib et les ajouter dans le CLASSPATH.
Vous trouverez les 2 fichiers suivant ici http://ormlite.com/releases/

– ormlite-android-X.XX.jar
– ormlite-core-X.XX.jar

Les Annotations

Pour configurer le mapping objet on utilise des annotations.
Les annotations principales sont :
- @DatabaseTable
- @DatabaseField

L’annotation @DatabaseTable se dépose devant la déclaration d’une classe métier, et prend en paramètre le nom de table sur laquelle on veut faire le mapping.

@DatabaseTable(tableName = "le_nom_de_ma_table")

L’annotation @DatabaseField permet de configurer le mapping entre une colonne d’une table SQL, et le membre d’une classe.
@DatabaseField peut prendre plusieurs paramètres :

  • columnName= « le_nom_de_ma_colonne » indique le nom de la colonne sur laquelle on fait le mapping
  • generatedId = true/false : indique que le membre de la classe se correspond à un champ index auto-incrémenté
  • canBeNull = true/false : indique si champ peut être null, et permet de générer des exceptions
  • defaultValue = « 0″ : indique une valeur par défaut si le membre n’a pas été affecté avant l’enregistrement en base de l’objet.
  • unique = true pour définir une colonne UNIQUE
  • uniqueIndexName = « mon_ensemble_unique » permet de déclarer qu’un ensemble de membres est unique. C’est utile si on dépose l’annotation au moins sur 2 membres de la classe

Voici un exemple de classe métier annoté:

@DatabaseTable(tableName = "category")
public class Category extends DataEnveloppe implements Parcelable{

	public static final String   COLUMN_CATID         = "catID";

	@DatabaseField(generatedId=true)
	private int mRowid;

	@DatabaseField(columnName = COLUMN_CATID, canBeNull=false)
	private String mCatID;

	@DatabaseField(columnName = "title", canBeNull=false)
	private String mTitle;
....
}

Implémentation de OrmLiteSqliteOpenHelper

Vous aurez besoin de créer une classe qui hérite de OrmLiteSqliteOpenHelper.
Cette classe gère la création de la base à la première exécution, et gère la migration de la base lors des mises à jour de l’application.

Pour faire cette gestion, des callback sont appelés automatiquement, il faut implémenter les deux méthodes suivantes:
- onCreate(SQLiteDatabase sqliteDatabase, ConnectionSource
connectionSource)

- onUpgrade(SQLiteDatabase database, ConnectionSource
connectionSource, int oldVersion, int newVersion).

Pour créer les tables, il suffit d’appeler une méthode utilitaire qui va créer pour nous la table associé à une classe ; à condition d’avoir mis les annotations sur cette classe.

Par exemple :

public class DatabaseHelper extends OrmLiteSqliteOpenHelper {

	...
	@Override
	public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
		try {
			EtLog.d(TAG, "Tables created in %s", DATABASE_NAME);
			TableUtils.createTable(connectionSource, Category.class);
			TableUtils.createTable(connectionSource, MonObjetMetier.class);

		} catch (SQLException e) {
			Log.e(TAG, "Can't create database", e);
			throw new RuntimeException(e);
		}
	}
	...

Pour gérer la mise à jour voici un exemple d’implémentation. Attention dans cet exemple, on va supprimer la table et la recréer. Ce n’est pas toujours ce que l’on veut faire. Si on veut garder les données de l’application lors de la mise à jour, il faudra implémenter un algorithme de migration des données.

	@Override
	public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource,
		int oldVersion, int newVersion) {
		try {
			TableUtils.dropTable(connectionSource, Category.class, true);
			onCreate(db);
		} catch (SQLException e) {
			Log.e(TAG, "Impossible to drop database", e);
			throw new RuntimeException(e);
		}
	}

Obtenir une instance de OpenHelperManager

Afin d’éviter d’ouvrir plusieurs connections simultanément à la même base, nous allons utiliser la classe OpenHelperManager qui va surveiller les utilisations du Helper.
Pour avoir un accès à une instance de OpenHelperManager il faut appeler getHelper() dans vos activités.
Pour avoir accès à cette méthode cela implique que vos activités doivent hériter de OrmLiteBaseActivity, OrmLiteBaseListActivity, OrmLiteBaseService, ou OrmLiteBaseTabActivity.

Cet héritage obligatoire peut être très contraignant. Notamment si vous utilisez une autre bibliothèque qui, elle aussi, demande à vos activités d’hériter d’une certaine classe.
Pour éviter de se retrouver bloqué ainsi, je vous conseille d’utiliser une autre méthode.
Il est possible de déclarer une classe comme OrmLiteSqliteOpenHelper avec la méthode statique suivante.

setOpenHelperClass(Class openHelperClass)

Il est nécessaire d’appeler cette méthode le plus tôt possible dans votre application, ainsi je recommande de le faire dans le onCreate() de votre classe Application.

Rappel:
Si vous ne le savez pas, il est possible d’avoir une instance qui représente le contexte de votre application. Il faut créer une classe qui hérite de Application et déclarer cette classe dans le manifeste. Je vous conseille de créer cette classe sous la forme d’un singleton.

    
...

Pensez à créer un assesseur pour obtenir l’instance au Helper il sera fournir par un appel à OpenHelperManager.getHelper()

Voilà ce que ça donne au niveau du code :

	private static MonApplication mInstance;
	private OrmLiteSqliteOpenHelper mOrmLiteHelper;

	@Override
	public void onCreate() {
		super.onCreate();
		mInstance = this;
        DataManager.getInstance().setApplicationContext(this);
        PreferencesManager.getInstance().setApplicationContext(this);
        FaceBookUtils.initContext(this);
        initData();
	}

	public static MonApplication getInstance() {
		return mInstance;
	}

	private void initData() {
        DataManager.getInstance().setOpenHelperClass(DatabaseHelper.class);
	}

	public OrmLiteSqliteOpenHelper getHelper() {

        if(mOrmLiteHelper == null) {
            mOrmLiteHelper = OpenHelperManager.getHelper(this);
        }
        return mOrmLiteHelper;
    }

	@Override
	public void onTerminate() {
		super.onTerminate();
		if (mOrmLiteHelper != null) {
			OpenHelperManager.releaseHelper();
		}
	}

Sauvegarder un objet métier

Ensuite pour faire un enregistrement d’une entité d’un objet métier, il suffit d’appeler la méthode save() que l’on aura implémenté comme ci-dessous dans notre classe métier :
Notez que c’est la même méthode pour un update ou un create .

	protected void save() throws SQLException {
		OrmLiteSqliteOpenHelper helper = getHelper();
		if (helper instanceof DatabaseHelper) {
			DatabaseHelper absHelper = (DatabaseHelper) helper;
			@SuppressWarnings("unchecked")
			Dao< MonObjetMetier, String> dao = (Dao< MonObjetMetier, String>) absHelper
					.getDao(getClass());
			dao.createOrUpdate(this);
		} else {
			Log.e("",
					"Object not created nor updated! Your Helper must inherit from AbstractDatabaseHelper.");
		}
	}

Et pour faire un select

Depuis l’objet Dao on appelle la méthode queryBuilder() pour obtenir un QueryBuilder ;
Ensuite il existe plusieurs méthodes utiles pour ajouter une clause WHERE, un LIMIT ou un ORDER BY.

Pour construite une clause WHERE il faudra utiliser également les méthodes disponibles depuis l’objet Where comme dans l’exemple ci-dessous.

public List getListCategoryFromDb() {
	List categoryListFromCache = new ArrayList();
	try {

		QueryBuilder queryBuilder = getDao().queryBuilder();

		@SuppressWarnings("rawtypes")
		Where where = queryBuilder.where();
		where.eq(Category.COLUMN_SUB_CAT, "1"); // Categorie de niveau 1

		PreparedQuery preparedQuery = queryBuilder.prepare();
		categoryListFromCache = getDao().query(preparedQuery);
		setList(categoryListFromCache);
	} catch (Exception e) {
		// TODO: handle exception
	}
	return categoryListFromCache;
}

Conclusion

Avec cette bibliothèque, une fois la classe OrmLiteSqliteOpenHelper implémentée, il ne vous reste plus beaucoup de lignes de code à écrire pour faire de l’enregistrement en base de donnée.
La méthode save() peut même être unifiée dans une classe parente de toutes vos classes métier(que vous avez besoin d’enregistrer en base).

shocq

shocq

19 jan
2012

Une librairie pratique pour parser un flux JSON sous android

Bonjour,

Je vais vous parler d’une librairie très pratique dans les projets Android, lorsque vous avez besoin d’interroger des services JSON.

Cette librairie permet de transformer (on dit dé-sérialiser) le flux JSON en objet métier.
On verra à la fin de cet article que l’on peut même faire l’inverse : c’est à dire transformer (dans ce cas on dit sérialiser) un objet métier en flux JSON.

Cette librairie s’appelle JacksonParser. Elle peut s’utiliser sur Android mais aussi dans projet JAVA standard.
Il existe évidemment d’autres librairies mais les avantages de JacksonParser sont :
– sa vélocité
– ses possibilités de configuration
– c’est open-source

Un petit rappel sur ce qu’est JSON:
JSON est un format d’échange de données,
– facile à écrire et à lire
– c’est un format léger
– facilement analysable par un programme

Je vous invite à vous rendre ici : http://jackson.codehaus.org/

et à télécharger
– jackson-core-asl-X.X.X.jar
– jackson-mapper-asl-X.X.X.jar

Une fois ceci fait il faut inclure les 2 fichiers jar dans votre projet et les ajouter au CLASSPATH.

Ensuite je vous conseille de construire vos objets métiers en fonction de la structure du flux JSON que vous lisez.

Voici l’exemple d’un flux JSON

{
	"typeList": "",
	"collabo": [
		{
			"nom": "Abdennadher", "prenom": "Walid"
		},
		{
			"nom": "Hocq", 	"prenom": "Sylvain"
		}

	]
}

Et voici comment j’ai créé mes objets métiers pour ce flux.

public class CollaborateurList {
	private List listCollabo;

	public List getListCollabo() {
		return listCollabo;
	}

	@JsonProperty("collabo")
	public void setListCollabo(List listCollabo) {
		this.listCollabo = listCollabo;
}} 

public class Collabo {
	String nom;
	@JsonProperty("prenom")
String prenom;

	@JsonProperty("nom")
	public void setNom(String nom) {  this.nom = nom; }

}

Le mapping entre flux JSON et objet métier, se configure à l’aide d’annotations.
Ici j’utilise uniquement l’annotation @JsonProperty.
Si vous avez bien regardé l’exemple ci-dessus, vous remarquerez que l’on peut poser ces annotations sur des setters mais aussi directement sur des propriétés (voir la propriété prénom ).

L’avantage de passer par des setters, c’est de pouvoir faire des conversions, par exemple: convertir une chaîne de caractères en date avant d’affecter un attribut de l’objet. Le désavantage c’est que c’est un peu plus verbeux…

Une fois les classes métier créées et configurées, il faut créer un objet ObjectMapper comme ceci :

	private static ObjectMapper sMapper = new ObjectMapper();
	static {
		sMapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
	}

Ici on configure le mapper pour qu’il ignore les propriétés sur lesquelles on n’a pas configuré de mapping. Sans cela on aurait une erreur.

Ensuite il n’y a plus qu’à appeler la méthode readValue avec le flux en paramètre et le type d’objet attendu.

String collaboJson = "";
CollaborateurList collaboList = null;
try {
	collaboJson = StreamUtils.inputStreamToString(getAssets().open("collabo.json"));
	collaboList = sMapper.readValue(collaboJson,CollaborateurList.class);
} catch (JsonParseException e) {
	e.printStackTrace();
} 

Remarque sur le type de retour

Dans notre exemple le flux commence par une structure c’est à dire une accolade {
Il faut dans ce cas spécifier le type de retour attendu : ici CollaborateurList.class

Si on a un flux commençant directement par un tableau c’est à dire par un [
Par exemple avec le flux suivant :

[
		{
			"nom": "Abdennadher", "prenom": "Walid"
		},
		{
			"nom": "Hocq", 	"prenom": "Sylvain"
		}

]

Dans ce cas on peut utiliser la manière suivante pour faire l’appel à readValue() :

Listlist = sMapper.readValue(collaboJson, new TypeReference>() { });

Et si on veut sérialiser...

Il est possible que vous souhaitiez faire le traitement inverse. Par exemple si vous avez déjà des instances d’objets métiers et que vous souhaitez les convertir en une chaîne JSON, pour par exemple les envoyer dans une requête HTTP POST.
Et bien dans ce cas, il suffit d’appeler la méthode writeValueAsString.

String json = sMapper.writeValueAsString(collaboList);

Dans ce cas il n’y a pas de complication, si l’objet à sérialiser est une liste d’objet métier ou une structure d’objet métier.

Si vous désirez utiliser des annotations sur des méthodes plutôt que sur des attributs (pour faire une transformation en amont par exemple). Il faudra, dans ce cas, déposer des annotations sur les getters.

Résumé :

Un petit résumé pour bien comprendre quand utiliser des getters et setters.
Avant tout, cela est nécessaire uniquement si vous avez besoin de faire une transformation, par exemple convertir une chaîne de caractères en type Date.

Si c’est le cas, il faut bien comprendre ceci: pour sérialiser, le parser à besoin de récupérer les propriétés des objets métier, donc il utilise les getters, donc il faut annoter les getters.
Et pour dé-sérialiser, le parser à besoin d’affecter les propriétés des objets métiers, donc il utilise les setters, qu’il faut annoter.

shocq

shocq