How to implement mysql "where ? is null " in SpringJDBCTemplate - mysql

I Want to implement a sql as follows in SpringJDBC
SELECT note from item WHERE sku = :sku AND make = :make
But the query can't find the record actually exist when make is a null property.
After debug,I found out there is a difference between " make = null " and " make is null " in MYSQL. But there is no API i can find which is suited for that situation in SpringJDBC. Any clues for that or must in static sql?

Try this:
public Item findBySkuAndMake(String sku, String make) {
Item item = null;
try {
String sql = "SELECT note FROM item WHERE sku = :sku AND make = :make";
SqlParameterSource parameters = new MapSqlParameterSource()
.addValue("sku", sku)
.addValue("make", make);
item = namedParameterJdbcTemplate.queryForObject(sql, parameters, new ItemRowMapper());
} catch (DataAccessExceptione) {
System.err.printf("Something went wrong while trying to search inside ITEM; this is why: %s%n", e);
}
// Then read getNote() ...or just return it (and change the method's return type)
return item;
}
// This is up to you, I don't know what's inside *Item*
private static class ItemRowMapper implements RowMapper<Item> {
#Override
public Item mapRow(ResultSet resultSet, int i) throws SQLException {
return new Item(resultSet.getString("note"), resultSet.getString("sku"),
resultSet.getString("make");
}
}
If you need to make one (or more) field(s) conditional, just add an if clause in the SQL query and exclude it (or them) from SqlParameterSource.

Related

Rename value within query

I have a problem with renaming a specific value in a column in mySQL Database. At first i thought i could just use 'AS' to rename, but i'm actually trying to rename a value in a column. My column is named FoundLost. In this column I Store values '0' and '1'. '0' is Found and '1' is Lost.
The reason I need to rename this values is because I use the data from this database to create a pieChart. with the function .getName it gives the names '0' and '1'.
I was hoping someone could help me out!
The class with the query is the code below:
public static ObservableList getPChartFoundLost() {
String query = "SELECT FoundLost, concat(round(count(FoundLost) *100 / (SELECT count(FoundLost) FROM Luggage))) AS percent FROM Luggage GROUP BY FoundLost";
ObservableList FoundLost = FXCollections.observableArrayList();
Connection connection = DatabaseUtils.connect();
if (connection != null) {
try {
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()) {
FoundLost.add(new PieChart.Data(resultSet.getString("FoundLost"), resultSet.getInt("percent")));
}
resultSet.close();
statement.close();
} catch (SQLException sqle) {
System.out.println(sqle.getMessage());
}
DatabaseUtils.disconnect(connection);
}
return FoundLost;
}
Controller:
public void clickPChartFoundLost(ActionEvent event) {
//PieChart
ObservableList FoundLost = StatisticsUtils.getPChartFoundLost();
pieChart.setVisible(true);
pieChart.setData(FoundLost);
pieChart.setTitle("Found and Lost luggage");
for (final PieChart.Data data : pieChart.getData()) {
data.getNode().addEventHandler(MouseEvent.ANY,
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
//Label vullen met data van Observable list uit Database
pieChartValueLable.setText(String.valueOf(data.getName()) + ": "
+ String.valueOf(data.getPieValue()) + "%");
}
});
}
}
Thanks!!
In your query, try this as your first column instead of just FoundLost. It translates your 0 and 1 values to meaningful strings for your chart. The rest of your query can stay the same.
IF(FoundLost = 0,'Found','Lost') AS FoundLost

Lazy loading with JPA criterea

I implemented a generic solution for using lazy loading primefaces datatables using JPA Criterea.
However I am still having some doubts with thie implemented solution whenever we deal with several Joins (say for example an entity User that has relation with other entities like Account, Address, Department.....in addition to raw type properties like: String username, Date birthdate...etc).
I tested this solution but I am having some delays while loading huge number of data (however the solution is supposed to load only a limited number of rows specified by PageSize coming from datatable), so:
How to improve the performance of this solution?
How to be sure the number of loaded data is the one specified in the Pagesize?
Can you check the count() method and tell if it counts the number of result rows without loading all the data?
And most importantly how to use this solution in order to be generic with filters coming from Search forms (I mean how to use this sae generic method and give search critereas from a search form with multi search fields)?
Please I need your answer on the above mentioned questions especially the last one.
Here is the code:
public <T extends Object> List<T> search(Class<T> type, int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters){
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<T> q = cb.createQuery(type);
Root<T> root=q.from(type);
q.select(root);
//Sorting
if (sortField != null && !sortField.isEmpty()) {
String[] sortingField = sortField.split("\\.", 2);
Path path = sortingField.length == 1 ? root.get(sortingField[0]): root.join(sortingField[0]).get(sortingField[1]);
if (sortOrder.equals(SortOrder.ASCENDING)) {
q.orderBy(cb.asc(path));
} else if (sortOrder.equals(SortOrder.DESCENDING)) {
q.orderBy(cb.desc(path));
}
}
// Filtering
Predicate filterCondition = cb.conjunction();
String wildCard = "%";
for (Map.Entry<String, String> filter : filters.entrySet()) {
String[] filterField = filter.getKey().split("\\.", 2);
Path path = filterField.length == 1 ? root.get(filterField[0]): root.join(filterField[0]).get(filterField[1]);
filterCondition = cb.and(filterCondition, filter.getValue().matches("[0-9]+")
? cb.equal(path, Long.valueOf(filter.getValue()))
: cb.like(path, wildCard + filter.getValue() + wildCard));
}q.where(filterCondition);
//Pagination
TypedQuery<T> s = entityManager.createQuery(q);
if (pageSize >= 0){
s.setMaxResults(pageSize);
}
if (first >= 0){
s.setFirstResult(first);
}
log.info("\n\n\n");
log.info("XXXXXXXXXxX");
log.info("=> CommonRepository - Total number of rows returned: ");
log.info("XXXXXXXXXXX");
log.info("\n\n\n");
return s.getResultList();
}
public <T extends Object> int count(Class<T> type, Map<String, String> filters){
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<T> root=cq.from(type);
// Filtering
Predicate filterCondition = cb.conjunction();
String wildCard = "%";
for (Map.Entry<String, String> filter : filters.entrySet()) {
String[] filterField = filter.getKey().split("\\.", 2);
Path path = filterField.length == 1 ? root.get(filterField[0]): root.join(filterField[0]).get(filterField[1]);
filterCondition = cb.and(filterCondition, filter.getValue().matches("[0-9]+")
? cb.equal(path, Long.valueOf(filter.getValue()))
: cb.like(path, wildCard + filter.getValue() + wildCard));
}cq.where(filterCondition);
cq.select(cb.count(root));
return entityManager.createQuery(cq).getSingleResult().intValue();
}

Existing posts keep on re-added into jTable with newer post

Here are my codes :
public void submitReply(ActionEvent e) {
String replyBy = userName;
String reply = jTextArea_reply.getText();
if (reply.equals("")) {
JOptionPane.showMessageDialog(null, "Comment cannot leave blank");
} else {
eForumTopics comment = new eForumTopics(replyBy, reply);
if (comment.createComment() == true) {
JOptionPane
.showMessageDialog(null,
"Reply submitreted successfully. You will be redirect to main page.");
SetUpJTableComment();
public void SetUpJTableComment() {
// Get jTable model
DefaultTableModel tableModel1 = (DefaultTableModel) jTableComment
.getModel();
// Store column data into Array (3 columns)
String[] data = new String[3];
// Set Up Database Source
db.setUp("IT Innovation Project");
String sql = "Select reply_content,reply_by from forumReplies WHERE reply_topic = "
+ topicId + "";
ResultSet resultSet = null;
// Call readRequest to get the result
resultSet = db.readRequest(sql);
try {
while (resultSet.next()) {
data[0] = resultSet.getString("reply_content");
data[1] = resultSet.getString("reply_by");
// Add data to table model
tableModel1.addRow(data);
}
resultSet.close();
} catch (Exception e) {
System.out.println(e);
}
// add tablemodel to jtable
}
The problem is whenever users post a new reply, the existing posts will be re-added together. I try to do like only the newer reply from the comment box will be added into the jTable instead of keep on re-add the existing posts with newer reply. What am I supposed to use? A for loop? Thanks in advance.
The correct way to delete the content of DefaultTableModel is
model.setRowCount(0);
vs. the evil way mentioned in the comment (won't repeat it here ;-) which violates two rules
never change the underlying datastructure of a model under its feet
never call any of the model's fireXX from code outside the model
If doing the latter seems to help, it's a waring signal: you either violated the former or your model implementation is incorrect

Passing multiple Include statements into a repository?

I am trying to figure out a way to pass a collection of include statements into my repository so that I can have it include specific entities. Below is some sample code from my repository.
public TEntity GetById(Guid id)
{
return id != Guid.Empty ? GetSet().Find(id) : null;
}
private IDbSet<TEntity> GetSet()
{
return _unitOfWork.CreateSet<TEntity>();
}
The GetByID method calls the GetSet to return the entity set. I was thinking, if I could somehow pass in a collection of entities to include (via an expression) as part of my GetById, this way I wouldn't have to expose the GetSet to my services. So, something like this:
var entity = _repository.GetById(theId, e => {e.Prop1, e.Prop2, e.Prop3});
I could then pass that expression into my GetSet method and pass it into an include statement. Thoughts?
I have done something like this in my code recently. Would the following work for you?
public TEntity GetById(Guid id, params Expression<Func<TEntity, object>>[] includeProperties)
{
if (id == Guid.Empty) return null;
var set = _unitOfWork.CreateSet<TEntity>();
foreach(var includeProperty in includeProperties)
{
set.Include(includeProperty);
}
return set.First(i => i.Id == id);
}
Then you would call it like this...
var entity = _repository.GetById(theId, e => e.Prop1, e=> e.Prop2, e=> e.Prop3);
I know this doesn't exactly follow your pattern, but I think you could refactor it as required.
I don't think Paige Cook's code will work quite as shown.
I've included a modified version of the code that should work instead:
public TEntity GetById(Guid id, params Expression<Func<TEntity, object>>[] includeProperties)
{
if (id == Guid.Empty) return null;
IQueryable<TEntity> set = _unitOfWork.CreateSet<TEntity>();
foreach(var includeProperty in includeProperties)
{
set = set.Include(includeProperty);
}
return set.First(i => i.Id == id);
}
I only spotted this by tracing the SQL generated by Entity Framework, and realised the original code was only giving the illusion of working, by using lazy-loading to populate the entities specified for inclusion.
There's actually a more terse syntax for applying the Include statements using the LINQ Aggregate method, which is in the blog post linked to. My post also improves the method slightly by having a fall-back to the Find method, when no includes are needed and also shows an example of how to implement a "GetAll" method, using similar syntax.
It's bad idea to store context in non-local space, for many reasons.
I modify Steve's code and get this for my ASP.NET MVC projects:
public aspnet_User FirstElement(Func<aspnet_User, bool> predicate = null, params Expression<Func<aspnet_User, object>>[] includes)
{
aspnet_User result;
using (var context = new DataContext())
{
try
{
var set = context.Users.AsQueryable();
for (int i = 0; i < includes.Count(); i++ )
set = set.Include(includes[i]);
if (predicate != null)
result = set.ToList().FirstOrDefault(predicate);
else
result = set.ToList().FirstOrDefault();
}
catch
{
result = null;
}
}
return result;
}
The include method can be strung together in your linq query like so:
var result = (from i in dbContext.TableName.Include("RelationProperty")
.Include("RelationProperty")
.Include("RelationProperty")
select i);

Find out what fields are being updated

I'm using LINQ To SQL to update a user address.
I'm trying to track what fields were updated.
The GetChangeSet() method just tells me I'm updating an entity, but doesn't tell me what fields.
What else do I need?
var item = context.Dc.Ecs_TblUserAddresses.Single(a => a.ID == updatedAddress.AddressId);
//ChangeSet tracking
item.Address1 = updatedAddress.AddressLine1;
item.Address2 = updatedAddress.AddressLine2;
item.Address3 = updatedAddress.AddressLine3;
item.City = updatedAddress.City;
item.StateID = updatedAddress.StateId;
item.Zip = updatedAddress.Zip;
item.Zip4 = updatedAddress.Zip4;
item.LastChangeUserID = request.UserMakingRequest;
item.LastChangeDateTime = DateTime.UtcNow;
ChangeSet set = context.Dc.GetChangeSet();
foreach (var update in set.Updates)
{
if (update is EberlDataContext.EberlsDC.Entities.Ecs_TblUserAddress)
{
}
}
Use ITable.GetModifiedMembers. It returns an array of ModifiedMemberInfo objects, one for each modified property on the entity. ModifiedMemberInfo contains a CurrentValue and OriginalValue, showing you exactly what has changed. It's a very handy LINQ to SQL feature.
Example:
ModifiedMemberInfo[] modifiedMembers = context.YourTable.GetModifiedMembers(yourEntityObject);
foreach (ModifiedMemberInfo mmi in modifiedMembers)
{
Console.WriteLine(string.Format("{0} --> {1}", mmi.OriginalValue, mmi.CurrentValue));
}
You can detect Updates by observing notifications of changes. Notifications are provided through the PropertyChanging or PropertyChanged events in property setters.
E.g. you can extend your generated Ecs_TblUserAddresses class like this:
public partial class Ecs_TblUserAddresses
{
partial void OnCreated()
{
this.PropertyChanged += new PropertyChangedEventHandler(User_PropertyChanged);
}
protected void User_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
string propertyName = e.PropertyName;
// do what you want
}
}
Alternatively, if you want to track a special property changing, you could use one of those OnPropertyNameChanging partial methods, e.g. (for City in your example):
partial void OnCityChanging(string value)
{
// value parameter holds a new value
}