I am trying to do an easy search on a table that can be on any kind of database. The following query is working an the most databases, but I cannot find a solution which works on mysql.
The tables in my database are generated by the active objects framework, so I cannot change the names or config of those instances.
Here is the query that works fine on all databases but MySQL:
select * from "AO_69D057_FILTER" where "SHARED" = true AND "CONTAINS_PROJECT" = true AND UPPER("FILTER_NAME") like UPPER('%pr%').
MySql is not able to use the table name in double quotes for some reason. If I use the unquoted table name it works on MySQL but not on Postgres. Postgres is converting the table name to lowercase because it is unquoted. AO is generating the table names in upper case.
I also tried to use an alias, but that can not work because of the evaluation hierarchy of the statement.
Any suggestions how to get rid of the table name problem?
By default double quotes are used to columns.
You can change it:
SET SQL_MODE=ANSI_QUOTES;
Here is the documentation about it:
http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html
I had the same problem. I select the query according to the exception I get. In the first call of the db search, I try without quotes if it fails then I try with quotes. Then I set useQueryWithQuotes variable accordingly so that in future calls I do not need to check the exception. Below is the code snipped I am using.
private Boolean useQueryWithQuotes=null;
private final String queryWithQuotes = "\"OWNER\"=? or \"PRIVATE\"=?";
private final String queryWithoutQuotes = "OWNER=? or PRIVATE=?";
public Response getReports() {
List<ReportEntity> reports = null;
if(useQueryWithQuotes==null){
synchronized(this){
try {
reports = new ArrayList<ReportEntity>( Arrays.asList(ao.find(ReportEntity.class, Query.select().where(queryWithoutQuotes, getUserKey(), false))) );
useQueryWithQuotes = false;
} catch (net.java.ao.ActiveObjectsException e) {
log("exception:" + e);
log("trying query with quotes");
reports = new ArrayList<ReportEntity>( Arrays.asList(ao.find(ReportEntity.class, queryWithQuotes, getUserKey(), false)));
useQueryWithQuotes = true;
}
}
}else{
String query = useQueryWithQuotes ? queryWithQuotes : queryWithoutQuotes;
reports = new ArrayList<ReportEntity>( Arrays.asList(ao.find(ReportEntity.class, query, getUserKey(), false)));
}
...
}
Related
Is it possible to automatically append database name to a table name in laravel?
The issue is that I have to join data from multiple databases in single queries and sometime I am having to manually replace template names, which is a lot of hassle.
The only solution that I found is that I can append database name to the table name within a model, i.e.
class User extends Model
{
protected $table = 'database_name.table_name';
}
But with above we are losing support for table prefixes.
Example when database name is not applied:
$userQuery = User::where('id', 1)
->with('settings')
->select('some data');
DB::connection('x')
->table('table-on-different-connection')
->insertUsing(['some columns'], $userQuery);
$userQuery is on a different connection and database_name was not applied to the tables within that part of the query. Hence why insertUsing is trying to perform joins on connection x.
Laravel is not appending database name when generating SQL statements. To resolve that, you need to create your own MySQL wrapper and append the database name to the table name that way.
This is where the issue takes place:
vendor\laravel\framework\src\Illuminate\Database\Query\Grammar.php
public function wrapTable($table)
{
if (! $this->isExpression($table)) {
return $this->wrap($this->tablePrefix.$table, true);
}
return $this->getValue($table);
}
You need to override wrapTable method and append database name to the table that way.
i.e.
public function wrapTable($table)
{
$databaseName = $this->wrap('my_database'); // dynamically defined name here
if (! $this->isExpression($table)) {
$tableName = $this->wrap($this->tablePrefix.$table, true);
return "{$databaseName}.{$tableName}";
}
return $this->getValue("{$databaseName}.{$table}");
}
How you go about extending Grammar and override this method depends on your application and your needs. This can be done globally (i.e. via AppProvider) or for an individual query.
Trying to replicate the following MySQL query in Hibernate using CriteriaBuilder. This query adds first and last name, removes all whitespaces in between and search for results that matches the given string.
select * from users where replace(concat(first_name, last_name), " ", "") like 'jamesbon%';
final CriteriaBuilder criteriaBuilder = getCurrentSession().getCriteriaBuilder();
final CriteriaQuery<UserImpl> userCriteriaQuery = criteriaBuilder.createQuery(UserImpl.class);
final Root<UserImpl> userRoot = userCriteriaQuery.from(UserImpl.class);
// criteriaBuilder.concat(userRoot .get("firstName"), userRoot .get("lastName"))
Concat is available through the builder, so all you really need to add is the replace function.
What you need to do is create a class that implements org.hibernate.boot.spi.MetadataBuilderInitializer and use it to register the functions with Hibernate. Let's say your class is called com.project.hibernate.MetaContrib
package com.project.hibernate;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.spi.MetadataBuilderInitializer;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.StringType;
public class MetaContrib implements MetadataBuilderInitializer {
#Override
public void contribute(MetadataBuilder metadataBuilder, StandardServiceRegistry serviceRegistry) {
metadataBuilder.applySqlFunction("str_replace", new StandardSQLFunction("replace", StringType.INSTANCE));
metadataBuilder.applySqlFunction("regex_replace", new StandardSQLFunction("REGEXP_REPLACE", StringType.INSTANCE));
}
}
The next step is to tell Hibernate to load this, by creating a file in the META-INF/services directory in your resources, called org.hibernate.boot.spi.MetadataBuilderInitializer. If such a directory doesn't exist, create it. The file has to contain the full name of the implementing class, and end in a new line.
Finally to use it:
expr1 = criteriaBuilder.concat(userRoot.get("firstName"), userRoot.get("lastName"));
expr2 = criteriaBuilder.function("str_replace", String.class, expr1, " ", "");
expr3 = criteriaBuilder.like(expr2, cb.parameter(String.class, "sv"));
userCriteriaQuery.where(expr3)
return createQuery(userCriteriaQuery)
.setParameter("sv", "jamesbon%")
.getResultList();
Detailed explanation:
The CriteriaBuilder creates a JPQL query. Your function expression becomes something like:
... WHERE function('str_replace', concat(u.firstName, u.lastName), ' ', '') LIKE :sv
Which when rendered to a native query will look like:
where replace(concat(u0.first_name, u0.last_name), ' ', '') like :sv
The function was registered under the name str_replace in JPQL, but it can be any name you choose. It's the name you give to the StandardSQLFunction constructor that tells it what the native name is.
Then further down the :sv internally becomes a ?, and when you use setParameter it tells the JDBC driver to safely send the string at that position.
However if you want to remove all whitespace, instead of merely all 0x20 space characters, you should use a regular expression like \s+ with the other function I put in MetaContrib. You can only do so if your MySQL is 8.0.4 or newer, or MariaDB 10.0.8 or newer. That function exists in the MariaDB10Dialect, so if you are using MariaDB, you may not need the MetaContrib class.
I want to execute the following select:
SELECT 0 as Value
What is the correlating syntax in LINQ for SQL?
Edit
I want to use the correlating LINQ for SQL statement in a Concat() call like this
var c = (from a in mytable select a.Value).Concat(select 0).Sum();
As you can see, Concat(select 0) obviously doesn't compile. Any ideas?
Edit 2
David suggested to use a simple collection instead. I've tried
private decimal[] mZeroDecimals = new[] { 0.0m };
...
public void MyFunction()
{
var c = (from a in mytable select a.Value).Concat(mZeroDecimals).Sum();
...
but it throws an exception Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator.
You're not actually querying anything, so there is no LINQ involved. You're just creating an anonymous object with a single property called Value:
var obj = new { Value = 0 };
Edit: Based on your comment, it sounds like you want this object in a collection. That doesn't make it a LINQ query (since you're still not querying anything), but you can declare a collection just as easily as a single object. Something like this:
var coll = new[] { new { Value = 0 } };
Since this is a collection, it can be used with any of the enumerable extension methods that LINQ uses, which sounds like what you're trying to do.
I am trying to update data to a mySQL database using JPA. I have no problem persisting data but flush will not work as expected. I retrieve the id for the login session, set that id (it is the primary key) along with setting the description field that I want merged to the database. I have debugged line by line through this method and all variables contain the expected values. Any ideas or suggestions to overcome this problem are appreciated.
public String update() {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();
if(true){
em.getTransaction().begin();
String sessionEmail=Util.getEmail();
//Create query to find user passwords matching the inputted name
Query myQuery = em.createQuery("SELECT u FROM BusinessAccount u WHERE u.email=:email");
myQuery.setParameter("email", sessionEmail);
List<BusinessAccount> accounts=myQuery.getResultList();
int intId=accounts.get(0).getId();
businessAccount.setId(intId);
String des=businessAccount.getDescription();
businessAccount.setDescription(des);
em.flush();
addMessage(new FacesMessage(FacesMessage.SEVERITY_INFO,
"User Registration Successful!", null));
return "success";
}
else {
addMessage(new FacesMessage(FacesMessage.SEVERITY_ERROR,
"User Registration Failed!", null));
return "failure";
}
}
merge() persists all the state of the entity. Not just the non-null fields. I it wasn't, you would complain that you want to set some field to null and that merge() ignores it and leaves it as is.
So get an entity from the database, and modify it, instead of only gettings its ID, creating a new entity instance from scratch and only settings some of its fields.
Note that, if you get the entity and modify it inside a single transaction, you don't even have to call merge(): the new state will be made persistent automatically.
Not sure if I should raise an issue regarding this, so thought I would ask if anybody knew a simple workaround for this first. I am getting an error when I try to use Dapper with OleDbConnection when used in combination with MS Access 2003 (Jet.4.0) (not my choice of database!)
When running the test code below I get an exception 'OleDbException : Data type mismatch in criteria expression'
var count = 0;
using (var conn = new OleDbConnection(connString)) {
conn.Open();
var qry = conn.Query<TestTable>("select * from testtable where CreatedOn <= #CreatedOn;", new { CreatedOn = DateTime.Now });
count = qry.Count();
}
I believe from experience in the past with OleDb dates, is that when setting the DbType to Date, it then changes internally the value for OleDbType property to OleDbTimeStamp instead of OleDbType.Date. I understand this is not because of Dapper, but what 'could' be considered a strange way of linking internally in the OleDbParameter class
When dealing with this either using other ORMs, raw ADO or my own factory objects, I would clean up the command object just prior to running the command and change the OleDbType to Date.
This is not possible with Dapper as far as I can see as the command object appears to be internal. Unfortunately I have not had time to learn the dynamic generation stuff, so I could be missing something simple or I might suggest a fix and contribute rather than simply raise an issue.
Any thoughts?
Lee
It's an old thread but I had the same problem: Access doesn't like DateTime with milliseconds, so you have to add and extension method like this :
public static DateTime Floor(this DateTime date, TimeSpan span)
{
long ticks = date.Ticks / span.Ticks;
return new DateTime(ticks * span.Ticks, date.Kind);
}
And use it when passing parameters:
var qry = conn.Query<TestTable>("select * from testtable where CreatedOn <= #CreatedOn;", new { CreatedOn = DateTime.Now.Floor(TimeSpan.FromSeconds(1)) });
Unfortunately, with current Dapper version (1.42), we cannot add custom TypeHandler for base types (see #206).
If you can modify Dapper (use the cs file and not the DLL) merge this pull request and then you do not have to use Floor on each parameters :
public class DateTimeTypeHandler : SqlMapper.TypeHandler<DateTime>
{
public override DateTime Parse(object value)
{
if (value == null || value is DBNull)
{
return default(DateTime);
}
return (DateTime)value;
}
public override void SetValue(IDbDataParameter parameter, DateTime value)
{
parameter.DbType = DbType.DateTime;
parameter.Value = value.Floor(TimeSpan.FromSeconds(1));
}
}
SqlMapper.AddTypeHandler<DateTime>(new DateTimeTypeHandler());