JPA - updating an embedded entity generates invalid SQL - sql

I am trying to update an embedded entity and JPA seems to generate the wrong SQL.
I have a Company entity with an embedded Logo entity
#Entity
public class Company {
private Long id;
#Embedded
private Logo logo;
// Omitted other fields, getters, setters, etc
}
#Embeddable
public class Logo {
private String fileName;
private String fileExtension;
private String imageDataType;
// Omitted getters and setters
}
In my DAO method I am trying to update the embedded logo like this:
#Override
public void setLogo(Logo logo, Long companyId) {
String q = "update Company c SET c.logo = :logo where c.id = :companyId";
Query query = entityManager.createQuery(q);
query.setParameter("companyId", companyId);
query.setParameter("logo", logo);
query.executeUpdate();
}
JPA (Hibernate actually) generates the following SQL.
update px_company set file_extension, file_name, file_type=(?, ?, ?) where id=?
Hibernate seems to understand it must update the three embedded logo fields, but it generates invalid SQL for it. The generated SQL results in an error.
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ' file_name, file_type=('jpg', '7679075394', 0) where id=1' at line 1
Any idea how I should update the embedded entity?

A bit old but just had the same issue - you should fully resolve properties of embedded classes in JPQL:
update Company c
SET c.logo.fileName = :fileName
,c.logo.fileExtension = :fileExtension
,c.logo.imageDataType= :imageDataType
where c.id = :companyId

Related

Apache Ignite : Ignite Repository query with "IN" clause, returns no records

I am using Apache Ignite as the back-end data store in a SpringBoot Application.
I have a requirement where I need to get all the entities whose name matches one of the names from a set of names.
Hence i am trying to get it implemented using a #Query configuration and a method named findAllByName(Iterable<String> names)as below:
Here on the Query, I am trying to use the 'IN' clause and want to pass an array of names as an input to the 'IN' clause.
#RepositoryConfig(cacheName = "Category")
public interface CategoryRepository extends IgniteRepository<Category, Long>
{
List<Category> findByName(String name);
#Query("SELECT * FROM Category WHERE name IN ( ? )")
Iterable<Category> findAllByName(Iterable<String> names); // this method always returns empty list .
}
In this the method findAllByName always returns empty list, even when ignite has Categories for which the name field matches the data passed in the query.
I am unable to figure out if there is a problem with the Syntax or the query of the method signature or the parameters.
Please try using String[] names instead for supplying parameters.
UPDATE: I have just checked the source, and we don't have tests for such scenario. It means that you're on uncharted territory even if it is somehow possible to get to work.
Otherwise looks unsupported currently.
I know your question is more specific to Spring Data Ignite feature. However, as an alternate, you can achieve it using the SqlQuery abstraction of Ignite.
You will form your query like this. I have pasted the sample below with custom sql function inSet that you will write. Also, the below tells how this is used in your sql.
IgniteCache<String, MyRecord> cache = this.ignite
.cache(this.environment.getProperty(Cache.CACHE_NAME));
String sql = "from “my-ignite-cache”.MyRecord WHERE
MyRecord.city=? AND inSet(?, MyRecord.flight)"
SqlQuery<String, MyRecord> sqlQuery = new SqlQuery<>(MyRecord.class,
sql);
sqlQuery.setArgs(MyCity, [Flight1, Flight2 ] );
QueryCursor<Entry<String, MyRecord>> resultCursor = cache.query(sqlQuery);
You can iterate the result cursor to do something meaningful from the extracted data.
resultCursor.forEach(e -> {
MyRecord record = e.getValue();
// do something with result
});
Below is the Ignite Custom Sql function which is used in the above Query - this will help in replicating the IN clause feature.
#QuerySqlFunction
public static boolean inSet(List<String> filterParamArgIds, String id) {
return filterParamArgIds.contains(id);
}
And finally, as a reference MyRecord referred above can be defined something like this.
public class MyRecord implements Serializable {
#QuerySqlField(name = "city", index = true)
private String city;
#QuerySqlField(name = "flight", index = true)
private String flight;
}

Ignite TextQueries on multiple fields

I have the following object:
#Data
#AllArgsConstructor
public class SearchItem implements Serializable {
private String id;
#QueryTextField
private String description;
#QueryTextField
private String brand;
}
As you can see I have two fileds enabled with the lucene index. Im confused now how to create the TextQuery to search only for a specific field or for both fields. I want to be able to make text searches in the brand field or the descritption field or in both. There is an ignite example showing how to create a TextQuery
private static void textQuery() {
IgniteCache<Long, Person> cache = Ignition.ignite().cache(PERSON_CACHE);
// Query for all people with "Master Degree" in their resumes.
QueryCursor<Cache.Entry<Long, Person>> masters =
cache.query(new TextQuery<Long, Person>(Person.class, "Master"));
// Query for all people with "Bachelor Degree" in their resumes.
QueryCursor<Cache.Entry<Long, Person>> bachelors =
cache.query(new TextQuery<Long, Person>(Person.class, "Bachelor"));
print("Following people have 'Master Degree' in their resumes: ", masters.getAll());
print("Following people have 'Bachelor Degree' in their resumes: ", bachelors.getAll());
}
But this example is only showing that I have to pass in the class and the search string. How can I define for which field of the class the search should be performed, when I have more then one field annotated with #QueryTextField?
Example search string:
description = ?, brand = ?
You can use query syntax from Apache Lucene: https://lucene.apache.org/core/2_9_4/queryparsersyntax.html
If it does not work with your field names, I would recommend using names in uppercase instead.

Nhibernate mapping at run time

I am developing a site in which nhibernate is using. that is working fine for static mapping. but problem that i apply this application on existing database. so is there any way that mapping of classes took place at run time. i mean user provide tables and column names for mapping. Thanks
From your question I interpret you saying that the POCO classes exists, but you don't know the table or column names at build time.
So, if you already had this class:
public class MyGenericClass
{
public virtual long Id { get; set; }
public virtual string Title { get; set; }
}
You could bind it to a table and columns at runtime:
string tableName; // Set somewhere else by user input
string idColumnName; // Set somewhere else by user input
string titleColumnName; // Set somewhere else by user input
var configuration = new NHibernate.Cfg.Configuration();
configuration.Configure();
var mapper = new NHibernate.Mapping.ByCode.ModelMapper();
mapper.Class<MyGenericClass>(
classMapper =>
{
classMapper.Table(tableName);
classMapper.Id(
myGenericClass => myGenericClass.Id,
idMapper =>
{
idMapper.Column(idColumnName);
idMapper.Generator(Generators.Identity);
}
);
classMapper.Property(c => c.Title,
propertyMapper =>
{
propertyMapper.Column(titleColumnName);
}
);
}
);
ISessionFactory sessionFactory = configuration.BuildSessionFactory();
ISession session = sessionFactory.OpenSession();
////////////////////////////////////////////////////////////////////
// Now we can run an SQL query over this newly specified table
//
List<MyGenericClass> items = session.QueryOver<MyGenericClass>().List();
I don't think that could be possibly with NHibernate, but you could use a workaround.
You could use a view instead a table for the NHibernate mapping.
And in runtime, you could create that View or update it with the especified user mapping you need.
For example, you define a mapping in NHibernate to a view named ViewMapped with two columns Name and Mail.
And in the other hand, the user has a table with three columns Name, SecondName, EMail.
you can create a view on runtime with the following select:
(SELECT Name + ' ' + SecondName as Name, EMail as Mail FROM tableName) AS ViewMapped
I hope that helps you, or at least leads you to a solution.

Hibernate filter not working while indexing through hibernate search

I am trying to index an embedded collection (Set) having one-to-many association using #IndexedEmbedded.The problem is that we are only soft deleting the records in our application and I want to apply hibernate filter on the indexed collection so as to exclude the logically deleted records while indexing.
#Index
Class A {
#IndexedEmbedded
#OneToMany(targetEntity = B.class, fetch = FetchType.EAGER)
#Filter(name = "deletedRecordsFilter")
Set<B> setOfBs;
}
For Indexing :
FullTextSession fts = getFullTextSession();
fts.createIndexer(entityClass)
.purgeAllOnStart(true)
.optimizeAfterPurge(true)
.optimizeOnFinish(true)
.batchSizeToLoadObjects(30)
.threadsForSubsequentFetching(8)
.threadsToLoadObjects(4)
.threadsForIndexWriter(3)
.startAndWait();
I have enabled the filter using session.enableFilter("deletedFilterRecords"). The data is indexed but the filter is not working properly. The embedded collections still contain the deleted records.
Is that hibernate filters do not work while indexing through hibernate search or am I missing something?
If filters do not work while indexing, then is there any way so as not to index the logically deleted records?
You should use a FullTextFilter, not a standard Hibernate filter. I have used one in the project that I am currently working on. You add an annotation like the below above the definition of your indexed class:
#Indexed
#Entity
#FullTextFilterDefs( {
#FullTextFilterDef(name = "includeInSearchFilter", impl = IncludeInSearchFilterFactory.class,
cache = FilterCacheModeType.INSTANCE_AND_DOCIDSETRESULTS)
})
public class SomeEntity ...
You then need to supply the referenced factory class as well, something like this:
public class IncludeInSearchFilterFactory {
private String includeInSearchResults;
public void setIncludeInSearchResults(String includeInSearchResults) {
this.includeInSearchResults = includeInSearchResults;
}
#Key
public FilterKey getKey() {
StandardFilterKey key = new StandardFilterKey();
key.addParameter(includeInSearchResults);
return key;
}
#Factory
public Filter getFilter() {
Query query = new TermQuery(new Term("includeInSearchResults", includeInSearchResults));
return new QueryWrapperFilter(query);
}
}
In my case the "includeInSearchResults" member was an indexed field on the entity which was set to true if I wanted the object to be returned by my search else it was set to false.
To enable the full text filter:
fullTextQuery.enableFullTextFilter("includeInSearchFilter").setParameter("includeInSearchResults", "true");

ReadAsDataContract exception while reading namespace

I'm trying to consume twitter's REST api mentioned at this link using WCF REST starter kit mentioned at this link.
I'm using the same objects in DataContract as mentioned in the article - statusList and status.
[assembly: ContractNamespace("", ClrNamespace = "TwitterShell")]
[CollectionDataContract(Name = "statuses", ItemName = "status")]
public class statusList : List<status> { }
public class user
{
public string id;
public string name;
public string screen_name;
}
public class status
{
public string id;
public string text;
public user user;
}
I'm reading the XML contents using ReadAsDataContract() method.
HttpClient http = new HttpClient("http://twitter.com/statuses/");
http.TransportSettings.Credentials =
new NetworkCredential("{username}", "{password}");
HttpResponseMessage resp = http.Get("friends_timeline.xml");
resp.EnsureStatusIsSuccessful();
statusList sList = resp.Content.ReadAsDataContract<statusList>();
And I get the following exception. I have not defined the following namespace at all.
Error in line 1 position 24. Expecting element 'statuses' from namespace 'http://schemas.datacontract.org/2004/07/sitename'.. Encountered 'Element' with name 'statuses', namespace ''.
Please help. Thanks.
Just don't do it. You are in for a world of pain if you try using Datacontracts and operation contracts to access non-wcf services.
Ok, so I guess that was a bit unfair leaving you high and dry without an alternative, so try this:
var response = client.Get("http://twitter.com/statuses/friends_timeline.xml");
var statuses = response.Content.ReadAsXElement();
var statusQuery = from st in statuses.Elements("status")
select new status {
id = st.Element("id").Value,
text = st.Element("text").Value,
user = (from us in st.Elements("user")
select new user {
id = us.Element("id").Value,
name = us.Element("name").Value,
screen_name = us.Element("screen_name").Value
}).FirstOrDefault()
};
var statuses = statusQuery.ToList();
Using Linq to XML to create objects from the XML document allows you to avoid the magic of serializers and completely control the names and datatypes of your client side objects. It would be really easy to wrap this as a new HttpContent extension method so that you could simply do:
var statuses = response.Content.ReadAsTwitterStatuses();
I came across your post searching for answers to the same problem and I was able to find a solution for what you are looking for if you want to forgo the LINQ to XML approach.
1) Make sure you annotate your Status class with the following decoration
[DataContract (Namespace = "")]
By specifying the above annotation, you are overriding the namespace from the default namespace of your class. This should fix your namespace problem.
2) To address the issues of nulls (which I also experienced), order of your fields are very important. When your objects are deserialized, it is done in alphabetically order. You can order your fields to match the order of the incoming XML using the Order property on the DataMember annotation.
e.g.
[DataMember (Order = 1)]
public string text
etc ...