I have an issue with the multiple Join using Spring JdbcTemplate if I need to make optional the input parameters.
This is the scenario.
The SQL table where i must perform the Join are:
CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(15),
password VARCHAR(20),
info VARCHAR(30),
active BOOLEAN,
locked BOOLEAN,
lockout_duration INT DEFAULT 0,
lockout_limit DATETIME,
login_attempts INT DEFAULT 0,
PRIMARY KEY(id)
);
CREATE TABLE profiles (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(15),
info VARCHAR(30),
PRIMARY KEY(id)
);
CREATE TABLE profiling(
user_id INT NOT NULL,
profile_id INT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE,
PRIMARY KEY(user_id,profile_id)
);
Where profiling is the table to associate a user with his profile; the profile must be intended as the identification of what actions are permitted to users.
In one my precedent post, i ask how to make optional the sql parameters and I obtained a perfect response, that works and i have always used since then. So, if i need to make this, is put in the where:
WHERE (is null or variabile_name = ?)
And, using jdbctemplate, i write:
jdbcTemplate.query(SQL, new Object[]{variabile_name, variabile_name}, mapper_name);
where, of course, SQL is the String object where i make the Query.
So, i make this also in this case, but i got the error:
java.lang.IllegalStateException: Failed to execute CommandLineRunner
Caused by: java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2).
I report the full method here:
/**
* This metod return the join between users and profiles
* made using the profiling table
*
* #param userID the user id code
* #param profilesID the profile id code
* #return the join list
*/
public List<Profiling> joinWithUsersandProfiles(Integer userID, Integer profileID)
{
//This is the mapper for Profiling
RowMapper<Profiling> profilingMapper = new RowMapper<Profiling>()
{
//This method must be implemented when we use a row mapper
public Profiling mapRow(ResultSet rs, int rowNum) throws SQLException
{
Profiling profiling = new Profiling();
profiling.setUser_id(rs.getInt("profiling.user_id"));
profiling.setProfile_id(rs.getInt("profiling.profile_id"));
//mapping users variabiles
profiling.setUsersId(rs.getInt("users.id"));
profiling.setUsersActive(rs.getBoolean("users.active"));
profiling.setUsersInfo(rs.getString("users.info"));
profiling.setUsersLocked(rs.getBoolean("users.locked"));
profiling.setUsersLockoutDuration(rs.getInt("users.lockout_duration"));
profiling.setUsersLockoutLimit(rs.getTime("users.lockout_limit"));
profiling.setUsersLoginAttempts(rs.getInt("users.login_attempts"));
profiling.setUsersName(rs.getString("users.name"));
profiling.setUsersPassword(rs.getString("users.password"));
//mapping profiles variabiles
profiling.setProfilesId(rs.getInt("profiles.id"));
profiling.setProfilesInfo(rs.getString("profiles.info"));
profiling.setProfilesName(rs.getString("profiles.name"));
return profiling;
}
};
/**
* This is the string that contain the query to obtain the data from profiling table.
* Please note that the notation "? is null or x = ?" means that the x variabile is optional;
* it can be asked as no making the query.
* If we give alla input equals null, it means that we must perform a SQl Select * From table.
*/
String SQL = "SELECT * FROM profiling JOIN users ON profiling.user_id = users.id JOIN profiles ON profiling.profile_id = profiles.id WHERE (is null or profiling.user_id = ?) AND (is null or profiling.profile_id = ?)";
/**
* The list containing the results is obtained using the method query on jdcbtemplate, giving in in input to it the query string, the array of object
* containing the input variabile of the method and the rowmapper implemented.
*/
List<Profiling> theProfilings = jdbcTemplate.query(SQL, new Object[]{userID, userID, profileID, profileID}, profilingMapper);
return theProfilings;
}
I know that the problem is made by the optional variabile. Why? if i try to remove the optional code and pass from:
(is null or variabile_name = ?)
to
variabile_name = ?
The code work perfectly.
So, what's my error here?
Edit: solved myself. I forgot the ? BEFORE the "is null" code. So, passing to:
(? is null or variabile_name = ?)
The code works.
Related
I am quite new to sqlalchemy, I guess I am missing just a little piece here.
There is this Database (sql):
create table CEO (
id int not null auto_increment,
name char(255) not null,
primary key(id),
unique(name)
);
create table Company (
id int not null auto_increment,
name char (255) not null,
ceo int not null,
primary key(id),
foreign key(ceo) references CEO(id)
);
That code:
from sqlalchemy import create_engine, Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import registry, relationship, Session
engine = create_engine(
"mysql+pymysql:xxxxxxxx",
echo=True,
future=True
)
mapper_registry = registry()
Base = mapper_registry.generate_base()
#####################
## MAPPING CLASSES ##
#####################
class CEO(Base):
__table__ = Table('CEO', mapper_registry.metadata, autoload_with=engine)
companies = relationship('Company', lazy="joined")
class Company(Base):
__table__ = Table('Company', mapper_registry.metadata, autoload_with=engine)
##########################
## FINALLY THE QUESTION ##
##########################
with Session(engine, future=True) as session:
for row in session.query(CEO).all():
for company in row.companies:
## Just the id of the Ceo is yielded here
print(company.ceo)
So CEO.companies works as expected, but Company.ceo does not, even though the FOREIGN KEY is defined.
What is a proper setup for the Company Mapper class, such that Company.ceo yields the related object?
I could figure out, that the automatic setup did not work, because the column Company.ceo exists in the Database and represents the ID of a given row. To make everything work, I needed to rename Company.ceo to Company.ceo_id and add the relation manually like so:
CompanyTable = Table('Company', Base.metadata, autoload_with=engine)
class Company(Base):
__table__ = CompanyTable
ceo_id = CompanyTable.c.ceo
ceo = relationship('CEO')
I would like to know if it would be possible to rename the column within the Table(…) call, such that I could get rid of the extra CompanyTable thing.
I am trying to check the database to see if the activity Name that I am entering already exists, if it exists I should get an error if it don't it should go ahead and insert values into database, below is the code I am trying but it isn't working properly btw the prevention of duplicates is already working cause of the UNIQUE KEY i give to activity Name but I also want an alert to let the users know that the activity name already exists.
function insertQueryDB(tx) {
var myDB = window.openDatabase("test", "1.0", "Test DB", 1000000);
tx.executeSql('CREATE TABLE IF NOT EXISTS dataEntryTb (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, activityName TEXT NOT NULL UNIQUE, location TEXT NOT NULL, time NOT NULL, date NOT NULL, reporter NOT NULL)');
var an = document.forms["myForm"]["activityName"].value;
var l = document.forms["myForm"]["location"].value;
var t = document.forms["myForm"]["time"].value;
var d = document.forms["myForm"]["date"].value;
var r = document.forms["myForm"]["reporter"].value;
if('COUNT activityName FROM dataEntryTb WHERE EXISTS activityName = "'+an+'" '){
navigator.notification.alert("activityName already Exists");
}
var query = 'INSERT INTO dataEntryTb ( activityName, location, time, date, reporter) VALUES ( "'+an+'", "'+l+'", "'+t+'", "'+d+'", "'+r+'")';
tx.executeSql(query,[]);
navigator.notification.alert("Retrieved the following: Activity Name="+an+" and Location="+l);
}
I think all you need to do is establish an unique key on activityName...
alter table dataEntryTb add unique key (activityName);
My current while statement is being used to get all the values from a table (two integer and one string value) and it looks like this:
while (reader.Read())
{
Int16 a = reader.GetInt16("id");
**int b = (reader["nullable_id"] != DBNull.Value) ? Convert.ToInt16(reader["nullable_id"]): 0;**
string c = reader.GetString("string");
Site page = new Site(a, b, c);
list.Add(page);
}
What I am using it to do is to GET all the values in a table. There's a primary key, a foreign key, and a regular string value (a, b, and c respectively). This works fine as is by allowing me to pull the primary and the string value while ignoring the foreign key that currently has null values. However, if I were to alter one of the foreign keys's value to 32 from 'null', the value won't return when I execute the GET method.
So my question is, how do I check whether or not the foreign key is null or not and then, if it is not null, it returns the value stored in the database and if it is null, then it leaves the value as null? I'm relatively new with using DBNull so I may be implementing it incorrectly.
simple change use this
while (reader.Read())
{
Int16 a = Convert.ToInt16(reader["id"]);
int b = (!string.IsNullOrEmpty(Convert.ToString(reader["nullable_id"]))) ? Convert.ToInt16(reader["nullable_id"]): 0;
string c = Convert.ToString(reader["string"]);
Site page = new Site(a, b, c);
list.Add(page);
}
I need to design the structure of a table that is going to store a log of events/actions for a project management website.
The problem is, these logs will be spelled differently depending on what the user is viewing
Example:
On the overview, an action could say "John F. deleted the item #2881"
On the single-item page, it would say "John F. deleted this item"
If the current user IS John F. it would spell "You deleted this item"
I'm not sure if I should store each different possibility in the table, this doesn't sound like the optimal approach.
For any kind of logs you can use the following table structure
CREATE TABLE logs (
id bigint NOT NULL AUTO_INCREMENT,
auto_id bigint NOT NULL DEFAULT '0',
table_name varchar(100) NOT NULL,
updated_at datetime DEFAULT NULL,
updated_by bigint NOT NULL DEFAULT '0',
updated_by_name varchar(100) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=3870 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
And then create another table to record what columns were updated exactly.
CREATE TABLE logs_entries (
id bigint NOT NULL AUTO_INCREMENT,
log_id bigint NOT NULL DEFAULT '0',
field_name varchar(100) NOT NULL,
old_value text,
new_value text,
PRIMARY KEY (id),
KEY log_id (log_id),
CONSTRAINT logs_entries_ibfk_1 FOREIGN KEY (log_id) REFERENCES logs (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=7212 DEFAULT CHARSET=utf8mb3
Now your table data look like this
Now Create a Database-View to fetch data easily in simple query
DELIMITER $$
ALTER ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW view_logs_entries AS
SELECT
le.id AS id,
le.log_id AS log_id,
le.field_name AS field_name,
le.old_value AS old_value,
le.new_value AS new_value,
l.auto_id AS auto_id,
l.table_name AS table_name,
l.updated_at AS updated_at,
l.updated_by AS updated_by,
l.updated_by_name AS updated_by_name
FROM (logs_entries le
LEFT JOIN logs l
ON ((le.log_id = l.id)))$$
DELIMITER ;
After creating database-view your data will look like this, so now you can easily query any logs of your project
You must have noticed that I have added updated_by and updated_by_name columns in logs table, Now there are two ways to fill this updated_by_name column
You can write a query on every log entry to fetch user name and store it(not recommended)
You can make a database-trigger to fill this column (Recommended)
You can create database trigger like this, it will automatically insert user name whenever logs entry added in database.
DELIMITER $$
USE YOUR_DATABASE_NAME$$
CREATE
TRIGGER logs_before_insert BEFORE INSERT ON logs
FOR EACH ROW BEGIN
SET new.updated_by_name= (SELECT fullname FROM users WHERE user_id = new.updated_by);
END;
$$
DELIMITER ;
After doing all this, you can insert entries in logs table whenever you do changes in any database table. In my case I have PHP-CodeIgniter project, and I did it like this in my Model file, here I am updating patient table of my database
public function update($id,$data)
{
// Log this activity
$auto_id = $id;
$table_name = 'patients';
$updated_by = #$data['updated_by'];
$new_record = $data;
$old_record = $this->db->where('id',$auto_id)->get($table_name)->row();
$data['updated_at'] = date('Y-m-d H:i:s');
$this->db->where('id', $id);
$return = $this->db->update($table_name,$data);
//my_var_dump($this->db->last_query());
if($updated_by)
{
$this->log_model->set_log($auto_id,$table_name,$updated_by,$new_record,$old_record);
}
return $return;
}
set_log function code checks which fields are actually changed
public function set_log($auto_id,$table_name,$updated_by,$new_record,$old_record)
{
$entries = [];
foreach ($new_record as $key => $value)
{
if($old_record->$key != $new_record[$key])
{
$entries[$key]['old_value'] = $old_record->$key;
$entries[$key]['new_value'] = $new_record[$key];
}
}
if(count($entries))
{
$data['auto_id'] = $auto_id;
$data['table_name'] = $table_name;
$data['updated_by'] = $updated_by;
$this->insert($data,$entries);
}
}
and insert log function look like this
public function insert($data,$entries)
{
$data['updated_at'] = date('Y-m-d H:i:s');
if($this->db->insert('logs', $data))
{
$id = $this->db->insert_id();
foreach ($entries as $key => $value)
{
$entry['log_id'] = $id;
$entry['field_name'] = $key;
$entry['old_value'] = $value['old_value'];
$entry['new_value'] = $value['new_value'];
$this->db->insert('logs_entries',$entry);
}
return $id;
}
return false;
}
You need to separate data from display. In your log, store the complete information (user xxx deleted item 2881). In your log viewer, you have the luxury of substituting as needed to make it more readable.
Given this schema:
Fruits
- FruitID INT PK
- FruitName NVARCHAR(30)
- FruitStatusID INT NULL FK: Statuses
Statuses
- StatusID INT PK
- StatusName NVARCHAR(30)
If I have a FruitID in hand (just an int, not a Fruit object), how do I update the FruitName and null out FruitStatusID without loading the Fruit object from the database first?
Note: this solution gets me pretty far, but I can't figure out how to null out a FK column.
Answers in C# or VB, thanks!
This works but seems unnecessarily complicated:
''//initialize the values I'm going to null out to something
Dim Tag As Data_Tag = New Data_Tag() With {
.Data_Tag_ID = DataTagID,
.Last_Error_DateTime = New DateTime(),
.Last_Error_Message = "",
.Last_Error_Severity_Type_ID = -1 }
''//start change tracking
DB.Data_Tags.Attach(Tag)
''//record changes to these properties (must be initialized above)
Tag.Last_Error_DateTime = Nothing
Tag.Last_Error_Message = Nothing
Tag.Last_Error_Severity_Type_ID = Nothing
DB.SubmitChanges()
Surely there's a better way!
(note: the weird comment syntax is solely for the code highliger--it doesn't like VB-style comments)