[Plugin] [WIP] NHibernate plugin for BlueG Mysql
#1

NHibernate plugin for BlueG Mysql

NHibernate is extension to BlueG Mysql plugin that automatically builds the queries by using mappings. To execute queries this plugin hooks internally part of BlueG's plugin natives, those native still can be used in AMX.
The idea took from NHibernate in C#. There are possibility that there are functions that exists in the original NHibernate but not exists in this plugin because not all of the functions can be done in SA-MP.
This plugin doesn't have working binaries yet because I just started building it.

Functions

I assume that this is just a peace from all function that will be supported. If you have any ideas to functions that should be included - send me private message or leave her comment.

Code:
// Sessions
native nh_create_factory(host[], db[], user[], password[], privileges_flags);
native nh_destory_factory(factory);
native nh_open_session(factory);
native nh_close_session(session);

// Mappings
native nh_table(table[]);
native nh_id(field[], enum_field);
native nh_map(field[], enum_field, length = 0); // If mapping an array (or string) use length argument too.
native nh_references(field, second_mapping, key);
native nh_has_many(field, second_mapping, key);

// Queries
native nh_select(connection, mapping);
native nh_join(connection, type, mapping, second_mapping);
native nh_exec(query, callback[] = "", format[] = "", ...);
native nh_save(connection, mapping, data[]);
native nh_update(connection, mapping, data[]);
native nh_save_or_update(connection, mapping, data[]);

// Operations
native nh_first(query);
native nh_single(query);
native nh_where(query, flags, field, value[]);
native nh_skip(query, field, value);
native nh_take(query, field, value);
native nh_order_by(query, field, asc = true);
native nh_group_by(query, field);
native nh_starts_with(query, field, value[]);
native nh_ends_with(query, field, value[]);
native nh_contains(query, field, value[]);

// Lazy loading
native nh_get(data, dest[]);
Enums
Code:
enum NH_PRIVILEGES {
	NH_PRIVILEGE_SELECT
	NH_PRIVILEGE_JOIN
	NH_PRIVILEGE_INSERT
	NH_PRIVILEGE_UPDATE
	NH_PRIVILEGE_DELETE
}
enum NH_WHERE {
	NH_WHERE_EQUALS
	NH_WHERE_BIGGER_THAN
	NH_WHERE_LESS_THAN,
	NH_WHERE_AND,
	NH_WHERE_OR
}
Example script

Code:
#include <a_samp>
#include <nhibernate>

#define MAX_PROPS 50
#define MAX_PROP_NAME 24

#define MAX_PASSWORD_LENGTH 25
#define MIN_PASSWORD_LENGTH 6

#define SQL_HOST 	   "localhost"
#define SQL_DB 		   "db"
#define SQL_READ_USER  "ruser"
#define SQL_READ_PASS  "rpassword"
#define SQL_WRITE_USER "ruser"
#define SQL_WRITE_PASS "rpassword"

enum {
    DIALOG_REGISTER,
	DIALOG_LOGIN,
	DIALOG_INF
}

new
	g_Connection_Read,
	g_Connection_Write;

enum e_Player {
	p_Id,
	p_Name[MAX_PLAYER_NAME],
	bool:IsRegistered,
	bool:IsLoggedIn,
	p_Kills,
	p_Deaths,
	p_Tag[20],
	p_Props[MAX_PROPS]
}
enum e_Prop {
	pr_Id,
	pr_Name[MAX_PROP_NAME],
	pr_Player
}

new
	g_Players[MAX_PLAYERS][e_Player],
	g_Props[MAX_PROPS][e_Prop];

main()
{
}

public OnGameModeInit()
{
    new read_factory =
        nh_create_factory(SQL_HOST, SQL_DB, SQL_READ_USER, SQL_READ_PASS, NH_PRIVILEGE_SELECT | NH_PRIVILEGE_JOIN);
    g_Connection_Read = nh_open_session(nh_create_factory);
    
    new write_factory =
        nh_create_factory(SQL_HOST, SQL_DB, SQL_WRITE_USER, SQL_WRITE_PASS, NH_PRIVILEGE_INSERT | NH_PRIVILEGE_UPDATE | NH_PRIVILEGE_DELETE);
    g_Connection_Write = nh_open_session(write_factory);
	return 1;
}

Map<e_Prop>() {
	nh_table("props");
	nh_id("id", pr_Id);
	nh_map("name", pr_Name, MAX_PROP_NAME);
	nh_map("player", pr_Player);
	return 1;
}

Map<e_Player>() {
	nh_table("accounts");
	nh_id("id", p_Id);
	nh_map("name", p_Name, MAX_PLAYER_NAME);
	nh_map("kills", p_Kills);
	nh_map("deaths", p_Deaths);
	nh_map("tag", p_Tag, 20);
	nh_has_many(p_Props, props, pr_Player);
	return 1;
}

public OnGameModeExit()
{
	nh_close_session(g_Connection_Read);
	nh_close_session(g_Connection_Write);
	return 1;
}

public OnPlayerConnect(playerid) {
    for(new i = 0; e_Player:i < e_Player; i++) {
	    g_Players[playerid][e_Player:i] = 0;
	}
	
	new
	    player_name[MAX_PLAYER_NAME];
	GetPlayerName(playerid, player_name, MAX_PLAYER_NAME);
	
	new
		player_query = nh_select(g_Connection_Read, "accounts");
	nh_first(player_query);
	nh_where(player_query, NH_EQUALS, p_Name, player_name);
	nh_exec(player_query, "OnPlayerConnect_", "d", playerid);
	return 1;
}

forward OnPlayerConnect_(data, playerid);
public OnPlayerConnect_(data, playerid) {
	if(!nh_is_null(data)) {
	    g_Players[playerid][IsRegistered] = true;
	    ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Welcome! Log-in Please!", "Welcome!\nIt appears that your account was found in our database!\nTherefore you may log-in to your account!\nEnter your password below:\n", "Log-in", "Quit");
	} else {
     	ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_INPUT, "Welcome! Register Please!", "Welcome!\nIt appears your account can not be found in our database.\nTherefore you may register your account and create a new one!\nPlease insert your password below:", "Register", "Quit");
	}
}

public OnPlayerDisconnect(playerid, reason) {
	nh_update(g_Connection_Write, g_Players[playerid]); // nh_update executes automaticlly, updated the record where identity is the same as g_Player[playerid][p_Id]
	return 1;
}

public OnPlayerDeath(playerid, killerid, reason)
{
    g_Players[playerid][Deaths]++;

	if(killerid != INVALID_PLAYER_ID) {
	    g_Players[killerid][Kills]++;
	}
    return 1;
}

public OnPlayerCommandText(playerid, cmdtext[])
{
    if(!g_Players[playerid][IsLoggedIn]) return SendClientMessage(playerid, -1, "You cannt send commands before signing in");
	return 0;
}
public OnPlayerRequestSpawn(playerid)
{
    if(!g_Players[playerid][IsLoggedIn]) return SendClientMessage(playerid, -1, "You cannt spawn before signing in");
 	return 1;
}

public OnPlayerText(playerid, text[])
{
    if(!g_Players[playerid][IsLoggedIn]) return SendClientMessage(playerid, -1, "You cannt use the chat before signing in"), 0;
	return 1;
}

forward OnPlayerLogin(data, playerid);
public OnPlayerLogin(data, playerid)
{
	if(!nh_is_null(data))
	{
	    g_Players[playerid][IsLoggedIn] = true;
		nh_get(data, g_Players[playerid]);
	} else {
	    ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Welcome! Login Please! - Incorrect PASSWORD!", "Welcome!\nIt appears that you haven't entered a CORRECT login password\nTherefore you must enter a CORRECT password\nPlease insert your password below:", "Log-in", "Quit");
	}
	return 1;
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
	if(dialogid == DIALOG_REGISTER)
	{
		if(!response) {
		    SendClientMessage(playerid, -1, "BB");
		    return SetTimerEx("OnPlayerKick", 100, false, "d", playerid);
		}
		else
		{
			if(!CheckPassword(inputtext)) {
				return ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_INPUT, "Welcome! Register Please! - No PASSWORD entered!", "Welcome!\nIt appears that you haven't entered a registration password\nIt also seems that you haven't entered anything at all.\nPlease insert your password below:", "Register", "Quit");
			}
			else
			{
			    GetPlayerName(playerid, g_Players[playerid][Name], MAX_PLAYER_NAME);
	   			nh_save(g_Connection_Write, "accounts", g_Players[playerid]);
			}
		}
		return 1;
	}
	if(dialogid == DIALOG_LOGIN)
	{
		if(!response) {
		    SendClientMessage(playerid, -1, "BB");
		    return SetTimerEx("OnPlayerKick", 100, false, "d", playerid);
		}
		else
		{
			if(!CheckPassword(inputtext)) {
				return ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Welcome! Login Please! - No PASSWORD entered!", "Welcome!\nIt appears that you haven't entered a login password\nIt also seems that you haven't entered anything at all.\nPlease insert your password below:", "Log-in", "Quit");
			}
			else
			{
			    new
				    player_name[MAX_PLAYER_NAME];
				GetPlayerName(playerid, player_name, MAX_PLAYER_NAME);

				new
					player_query = nh_select(g_Connection_Read, "accounts");
				nh_first(player_query);
				nh_where(player_query, NH_EQUALS, p_Name, player_name);
				nh_where(player_query, NH_EQUALS | NH_AND, -1, inputtext, "password");
				nh_exec(player_query, "OnPlayerLogin", "d", playerid);
			}
		}
		return 1;
	}
	return 0;
}

forward OnPlayerKick(playerid);
public OnPlayerKick(playerid)
{
    Kick(playerid);
}

stock CheckPassword(password[]) {
	return strlen(password) >= MIN_PASSWORD_LENGTH && strlen(password) <= MAX_PASSWORD_LENGTH;
}
This script is only an example, there are many missing things in this script.

Source & Download

Source: https://github.com/zilberman-rafael/...sql-nhibernate
Downloads: - (WIP)

Suggestions

Any suggestions is welcome.

License

Copyright 2014 Rafael

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

This repository using code from SA-MP GDK project, the full project license can be found at SA-MP GDK License.txt. limitations under the License.
Reply
#2

So, where's the difference between this and the existing ORM system in the MySQL plugin?
Reply
#3

Quote:
Originally Posted by maddinat0r
View Post
So, where's the difference between this and the existing ORM system in the MySQL plugin?
I searched but I can't find any functions like in this plugin in the Mysql plugin.
Mysql plugin is allowing you only executing queries, here the plugin generates the queries automatically (using the mappings).
Also I want it to support "Lazy loading" (I currently don't know if its possible...).
Reply
#4

There's a tutorial about the ORM system here and here are all the functions listed in the include.
I'm not quite sure if lazy initialization is possible, you'd have to know when a variable is used.
But I'm also not sure if lazy init is a good approach here. We have OnGameModeInit to initialize all variables/systems and we also don't really care if a gamemode is taking long to load. In addition to that, lazy init could cause evil lags while players are ingame, and that's really bad.
IMO it's better if OnGameModeInit takes one minute to initialize all systems than several initializations (and thus several possible lags) during gaming on the server.
Reply
#5

Quote:
Originally Posted by maddinat0r
View Post
There's a tutorial about the ORM system here and here are all the functions listed in the include.
I'm not quite sure if lazy initialization is possible, you'd have to know when a variable is used.
But I'm also not sure if lazy init is a good approach here. We have OnGameModeInit to initialize all variables/systems and we also don't really care if a gamemode is taking long to load. In addition to that, lazy init could cause evil lags while players are ingame, and that's really bad.
IMO it's better if OnGameModeInit takes one minute to initialize all systems than several initializations (and thus several possible lags) during gaming on the server.
My plugin will have more options than the ORM of BlueG's plugin (ex: relations between tables - hasmany, reference, etc..., it not based on column names - it based on the enum, some operations on the queries like where, single, first ans so on).

About the lazy loading, I think that it worth the try.
Reply
#6

The MySQL Plugin is not more developed by BlueG Since R8, It's currently developed by maddiant0r.
Reply
#7

It's better to use Mysql. Good job on effort anyways.
Reply
#8

Quote:
Originally Posted by SanAndreasMP
View Post
It's better to use Mysql. Good job on effort anyways.
I think that you didn't get the point. This is wrapper to mysql. In the inside it actually using the mysql plugin.
For the next time: read the topic before you post messages.
Reply
#9

Quote:

In the inside it actually using the mysql plugin

Which version? The old R7? Or R33 And/Or up?
Reply
#10

Quote:
Originally Posted by iFarbod
View Post
Which version? The old R7? Or R33 And/Or up?
The version that you using in your server. This plugin isn't using code from mysql plugin inside, its hooking the function that needed to the plugin to work and using them, you can choose what version to use. Currently you need version that supports cache.

Also: the mysql plugin should be added in the server.cfg too.
Reply
#11

Quote:
Originally Posted by Swimor
Посмотреть сообщение
The version that you using in your server. This plugin isn't using code from mysql plugin inside, its hooking the function that needed to the plugin to work and using them, you can choose what version to use. Currently you need version that supports cache.

Also: the mysql plugin should be added in the server.cfg too.
So it will be R33 And up. (probably R39-2)
Reply
#12

Quote:
Originally Posted by iFarbod
Посмотреть сообщение
So it will be R33 And up. (probably R39-2)
Yes, but you can choose what exactly version you want.
Reply
#13

Good work on this, +REP, But why making thinks harder? What's wrong with ORM? What's wrong with Cache?

Nevermind, Good job, tho.
Reply
#14

Quote:
Originally Posted by iFarbod
Посмотреть сообщение
Good work on this, +REP, But why making thinks harder? What's wrong with ORM? What's wrong with Cache?

Nevermind, Good job, tho.
There is nothing wrong, there are 2 reasons why I chose to make this pugin:
a) It has more features then the ORM in mysql plugin, you can compare the native list of this plugin and the native list of mysql plugin.
b) I love the NHibernate style. (mappings)

Also, I will try to impl Lazy loading on this plugin, maybe not the same as NHibernate on C# by I think that I can make something similar.

BTW: I will update Github soon.
Reply


Forum Jump:


Users browsing this thread: 3 Guest(s)