Accès à une base de données SQL Server via les templates OLEDB


La librairie ATL fournit un ensemble de templates et de classes C++ pour faciliter l'accès aux sources de données via OLE DB. La première chose à faire est d'établir une liaison à un provider en lui fournissant les paramètres demandés. Dans le cas du provider d'accès à SQL Server, il faut préciser le nom du serveur, le nom de la base de données et le type de sécurité (Windows ou non). Une fois le provider activé, il faut ouvrir une session. A ce stade, l'exécution de requêtes est autorisée.
	HRESULT hr;
	CDataSource connection;
	CSession session;

	CDBPropSet propset(DBPROPSET_DBINIT);
	propset.AddProperty(DBPROP_INIT_DATASOURCE, L"pex18");
	propset.AddProperty(DBPROP_INIT_CATALOG, L"pubs");
	propset.AddProperty(DBPROP_AUTH_INTEGRATED, L"SSPI");
	hr = connection.Open("SQLOLEDB", &propset);
	if( FAILED(hr) )
		return;
	hr = session.Open(connection);
	if( FAILED(hr) )
		return;
Les réquêtes peuvent être de type paramétrés ou non. La récupération des données est faite dans un ensemble de données : le recordset. Pour manipuler cet ensemble, il faut obtenir un accessor.  La base de données utilisée est la base installée par défaut, PUBS. Les requêtes se font sur la table AUTHORS.
select au_id, au_lname, au_fname from authors
select au_id, au_lname, au_fname from authors where state=?
{ ? = CALL sp_GetAuthorsByState (?,?) }
-- code de la procédure stockée
create procedure sp_GetAuthorsByState @pstate varchar(20), @pcount int output
as
	select au_id, au_lname, au_fname from authors where state = @pstate
	select @pcount = @@ROWCOUNT
	return @@ERROR
La procédure stockée sp_GetAuthorsByState prend 2 paramètres, un en entrée et un en sortie. Chaque requête est associée à une classe dans laquelle figurent les champs à récupérer et les champs paramètre.
class CAUTHORS
{
public:
	CAUTHORS() { *id = *lname = *fname = 0; }
	TCHAR id[25];
	TCHAR lname[33];
	TCHAR fname[33];
	
	BEGIN_COLUMN_MAP(CAUTHORS)
		COLUMN_ENTRY_TYPE(1, DBTYPE_STR, id)
		COLUMN_ENTRY_TYPE(2, DBTYPE_STR, lname)
		COLUMN_ENTRY_TYPE(3, DBTYPE_STR, fname)
	END_COLUMN_MAP()
};

class CAUTHORS_BYSTATE
{
public:
	CAUTHORS_BYSTATE() { *id = *lname = *fname = 0; }
	TCHAR id[25];
	TCHAR lname[33];
	TCHAR fname[33];
	TCHAR state[10];
	
	BEGIN_COLUMN_MAP(CAUTHORS_BYSTATE)
		COLUMN_ENTRY_TYPE(1, DBTYPE_STR, id)
		COLUMN_ENTRY_TYPE(2, DBTYPE_STR, lname)
		COLUMN_ENTRY_TYPE(3, DBTYPE_STR, fname)
	END_COLUMN_MAP()

	BEGIN_PARAM_MAP(CAUTHORS_BYSTATE)
		SET_PARAM_TYPE(DBPARAMIO_INPUT)
		COLUMN_ENTRY_TYPE(1, DBTYPE_STR, state)
	END_PARAM_MAP()
};

class CSP_AUTHORS_BYSTATE
{
public:
	TCHAR id[11];
	TCHAR lname[40];
	TCHAR fname[20];
	TCHAR param[20];
	long m_retParam, m_outParam;

BEGIN_PARAM_MAP(CSP_AUTHORS_BYSTATE)
	SET_PARAM_TYPE(DBPARAMIO_OUTPUT)
	COLUMN_ENTRY(1, m_retParam)
	SET_PARAM_TYPE(DBPARAMIO_INPUT)
	COLUMN_ENTRY(2, param)
	SET_PARAM_TYPE(DBPARAMIO_OUTPUT)
	COLUMN_ENTRY(3, m_outParam)
END_PARAM_MAP()

BEGIN_COLUMN_MAP(CSP_AUTHORS_BYSTATE)
	COLUMN_ENTRY_TYPE(1, DBTYPE_STR, id)
	COLUMN_ENTRY_TYPE(2, DBTYPE_STR, lname)
	COLUMN_ENTRY_TYPE(3, DBTYPE_STR, fname)
END_COLUMN_MAP()
};
Lorsque la connexion est établie, il ne reste plus qu'à exécuter la requête désirée et à parcourir le recordset.
	CCommand< CAccessor <CAUTHORS> > cmd;
	hr = cmd.Open(session,_T("select au_id, au_lname, au_fname from authors"));
	while ((hr = cmd.MoveNext()) == S_OK) 
	{
		printf("%s: %s %s\n", cmd.id, cmd.lname, cmd.fname);
	}
	cmd.Close();
Dans le cas d'une requête paramétrée, il faut donner une valeur à chaque paramètre associé par un ? dans la commande SQL.
	CCommand< CAccessor <CAUTHORS_BYSTATE> > cmdst;
	strcpy(cmdst.state, "CA");
	hr = cmdst.Open(session,_T("select au_id, au_lname, au_fname from authors where state=?"));
	while ((hr = cmdst.MoveNext()) == S_OK) 
	{
		printf("%s: %s %s\n", cmdst.id, cmdst.lname, cmdst.fname);
	}
	cmdst.Close();
Dans le cas d'une procédure stockée, on procède de la manière qu'avec une requête paramétrée, seulement il faut être certain d'avoir récupérer l'ensemble des recordsets avant d'obtenir les paramètres en sortie.
	CCommand<CAccessor<CSP_AUTHORS_BYSTATE>, CRowset, CMultipleResults > sp;
	strcpy(sp.param, "CA");
	hr = sp.Open(session,"{ ? = CALL sp_GetAuthorsByState (?,?) }");
	if( FAILED(hr) )
		return;
	while ((hr = sp.MoveNext()) == S_OK) 
	{
		printf("%s: %s %s\n", sp.id, sp.lname, sp.fname);
	}
	long pulRowsAffected = 0;
	while(sp.GetNextResult(&pulRowsAffected) == S_OK);
	printf("Count Value: %ld\n", sp.m_outParam);
	printf("Return Value: %ld\n", sp.m_retParam);
	sp.Close();
Il est possible d'invoquer une procédure stockée sans avoir à déclarer de classe dans lequel est décrit les paramètres et les champs à récupérer. Il suffit juste de connaître leurs noms.
	CCommand<CDynamicParameterAccessor,CRowset,CMultipleResults> sp2;
	hr = sp2.Create(session,"{ ? = CALL sp_GetAuthorsByState (?,?) }");
	hr = sp2.Prepare();
	void * pDummy = NULL;
	hr = sp2.BindParameters(&(sp2.m_hParameterAccessor),sp2.m_spCommand,&pDummy);
	
	char * pStateState;
	pStateState = (char *)sp2.GetParam("@pstate");
	strcpy(pStateState, "CA");

	hr=sp2.Open(NULL,NULL,0);
	sp2.Bind();
	hr = sp2.MoveFirst();
	while (hr == S_OK)
	{
		printf("%s %s %s\n", sp2.GetValue("au_id"), sp2.GetValue("au_lname"), sp2.GetValue("au_fname"));
		hr = sp2.MoveNext();
	}
	long lOutVal = 0;
	while( sp2.GetNextResult(&lOutVal) == S_OK );
	
	long lCount;
	sp2.GetParam("@pcount", &lCount);
	long lReturn;
	sp2.GetParam("@RETURN_VALUE", &lReturn);
	printf("Count Value: %ld\n", lCount);
	printf("Return Value: %ld\n", lReturn);

 
© 2001 Christophe Pichaud. All rights reserved.