Spring JPA repository findOne() with ID column called "name" - spring-data

I can't find anything in the docs about this but is findOne()/getOne() simply and alias for findById()?
I have an entity which looks like this
public class Tag {
#Id
private String name;
...
}
and a repository like this:
#Repository
public interface TagRepository extends JpaRepository<Tag, String> {
Tag findByName(String name);
}
this code works fine:
void saveTag() {
Tag tag = repository.findByName("test");
if (tag == null)
tag = new Tag("test");
tag.save();
}
saveTag();
saveTag();
However this blows up:
void saveTag() {
Tag tag = repository.findOne("test");
if (tag == null)
tag = new Tag("test");
tag.save();
}
saveTag();
saveTag();
At this point I get a DuplicateKeyException because the call to findOne() always returns null even though it exists in the DB

Related

Represent a single-rowed table in EF?

I have a configuration table in my database and it contains exactly one row.
ConfirmedScheduleColor | OverlappedScheduleColor | ColN
Currently, I'm retrieving the configuration like this:
var db = new SchedulingDbContext();
var config = db.Configurations.FirstOrDefault();
It's currently working fine and I can access my configurations and all. The thing is, the code looks awkward since I'm accessing the Configurations DbSet as if it contains many records (FirstOrDefault()); although actually, it contains only one record. I want to access my configurations like I'm accessing a static object. How to do that in EF?
You could simply add a property to your DbContext that returns Configurations.FirstOrDefault() and privatize the DbSet:
public class SchedulingDbContext : DbContext
{
private DbSet<Configuration> Configurations { get; set; }
public Configuration Configuration
{
get
{
return Configurations.FirstOrDefault();
}
}
}
I have a class in my project that has static methods to retrieve config settings. I use the ConfigurationManager rather than the database, but you could adapt it to get the setting from wherever you are storing the value.
In my example I have written a GetFromDb method for you that takes a key as parameter but that is because if I was storing my config settings in the database I wouldn't want to add a column every time I needed a new config setting. I would have a table with Key/Value columns. If you are wedded to the single row table then you might want to do without such a method.
public class Config
{
private _ConfirmedScheduleColor;
public static string ConfirmedScheduleColor
{
get
{
if(_ConfirmedScheduleColor == null)
_ConfirmedScheduleColor = GetFromDb("ConfirmedScheduleColor");
return _ConfirmedScheduleColor;
}
}
public static string OverlappedScheduleColor
{
get { return GetValue("OverlappedScheduleColor", "Pink"); }
}
public static int ColN
{
get { return GetValue("ColN", 2); }
}
private static string GetFromDb(string key)
{
if(key == "ConfirmedScheduleColor")
{
var config = db.Configurations.FirstOrDefault();
return config.ConfirmedScheduleColor;
}
}
private static string GetValue(string key, string defaultValue)
{
return ConfigurationManager.AppSettings[key] ?? defaultValue;
}
private static string GetValue(string key, int defaultValue)
{
int i;
if(int.TryParse(ConfigurationManager.AppSettings[key], out i))
return i;
return defaultValue;
}
}
In EF Core you can set the check constraint for the primary key. It enforces that column Id must have value that is equal to 1 which means only one record can exist in table if you have the primary key.
modelBuilder.Entity<YourTable>(e =>
{
e.HasCheckConstraint("CK_Table_Column", "[Id] = 1");
e.HasData(...) //optionally add some initial date for Id = 1
});

JPA composite PK is not working with 'finder' in play framework

I have a model called 'UserRoleHolder' like below.
#Entity
public class UserRoleHolder extends Model implements RoleHolder {
private static final long serialVersionUID = 1L;
#EmbeddedId
public UserRoleHolderPK userRoleHolderPK;
public List<UserPermission> permissions;
public List<UserRole> roles;
....
I made a composite PK called UserRoleHolderPK and it contains two foreign keys like below.
#Embeddable
public class UserRoleHolderPK {
#Basic
public Long userId;
#Basic
public Long projectId;
public UserRoleHolderPK(Long userId, Long projectId) {
this.userId = userId;
this.projectId = projectId;
}
public boolean equals(Object object) {
if (object instanceof UserRoleHolderPK) {
UserRoleHolderPK userRoleHolderPK = (UserRoleHolderPK) object;
return userId == userRoleHolderPK.userId && projectId == userRoleHolderPK.projectId;
} else {
return false;
}
}
public int hashCode() {
return (int) (userId + projectId);
}
}
userId and projectId are from other Models. (User.java and Project.java)
Then, in 'UserRoleHolder' class, I made a method called 'findRolesById' like below.
public static List<? extends Role> findRolesById(Long userId, Long projectId) {
return find
.where()
.eq("userRoleHolderPK", new UserRoleHolderPK(userId, projectId))
.findUnique().roles;
}
However, when I tried to run a test code like below, I encountered serious errors.
#Test
public void findRolesById() {
// Given
// When
#SuppressWarnings("unchecked")
List<UserRole> list = (List<UserRole>) UserRoleHolder.findRolesById(1l, 1l);
// Then
assertThat(list.get(0).name).isEqualTo("manager");
}
Errors are like,
'Syntax error in SQL statement "SELECT T0.USER_ID C0, T0.PROJECT_ID C1 FROM USER_ROLE_HOLDER T0 WHERE T0.NULL[*] = ? "; expected "identifier"; SQL statement: select t0.user_id c0, t0.project_id c1 from user_role_holder t0 where t0.null = ? [42001-158]
Bind values:[null]
Query was:
select t0.user_id c0, t0.project_id c1 from user_role_holder t0 where t0.null = ?
I think I missed some serious and basic stuff when I used JPA. Please, let me know what is the problem.
I think your problem is that you are trying to compare the Embeddedid object and not its fields, I don't think that the program will be smart enough as to know how to convert an user object comparison (the equals) to sql, so you might want to try something like this:
public static List<? extends Role> findRolesById(Long userId, Long projectId) {
return find
.where()
.eq("userRoleHolderPK.userId", userId)
.eq("userRoleHolderPK.projectId", projectId)
.findUnique().roles;
}

How to decorate a class item to be an index and get the same as using ensureIndex?

I'd like to define in class declaration which items are index, something like:
public class MyClass {
public int SomeNum { get; set; }
[THISISANINDEX]
public string SomeProperty { get; set; }
}
so to have the same effect as ensureIndex("SomeProperty")
Is this possible?
I think this is a nice idea, but you have to do this yourself, there's no built-in support for it. If you have an access layer you can do it in there. You'd need an attribute class, something like this;
public enum IndexConstraints
{
Normal = 0x00000001, // Ascending, non-indexed
Descending = 0x00000010,
Unique = 0x00000100,
Sparse = 0x00001000, // allows nulls in the indexed fields
}
// Applied to a member
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class EnsureIndexAttribute : EnsureIndexes
{
public EnsureIndex(IndexConstraints ic = IndexConstraints.Normal) : base(ic) { }
}
// Applied to a class
[AttributeUsage(AttributeTargets.Class)]
public class EnsureIndexesAttribute : Attribute
{
public bool Descending { get; private set; }
public bool Unique { get; private set; }
public bool Sparse { get; private set; }
public string[] Keys { get; private set; }
public EnsureIndexes(params string[] keys) : this(IndexConstraints.Normal, keys) {}
public EnsureIndexes(IndexConstraints ic, params string[] keys)
{
this.Descending = ((ic & IndexConstraints.Descending) != 0);
this.Unique = ((ic & IndexConstraints.Unique) != 0); ;
this.Sparse = ((ic & IndexConstraints.Sparse) != 0); ;
this.Keys = keys;
}
}//class EnsureIndexes
You could then apply attributes at either the class or member level as follows. I found that adding at member level was less likely to get out of sync with the schema compared to adding at the class level. You need to make sure of course that you get the actual element name as opposed to the C# member name;
[CollectionName("People")]
//[EnsureIndexes("k")]// doing it here would allow for multi-key configs
public class Person
{
[BsonElement("k")] // name mapping in the DB schema
[BsonIgnoreIfNull]
[EnsureIndex(IndexConstraints.Unique|IndexConstraints.Sparse)] // name is implicit here
public string userId{ get; protected set; }
// other properties go here
}
and then in your DB access implementation (or repository), you need something like this;
private void AssureIndexesNotInlinable()
{
// We can only index a collection if there's at least one element, otherwise it does nothing
if (this.collection.Count() > 0)
{
// Check for EnsureIndex Attribute
var theClass = typeof(T);
// Walk the members of the class to see if there are any directly attached index directives
foreach (var m in theClass.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
{
List<string> elementNameOverride = new List<string>(1);
EnsureIndexes indexAttr = null;
// For each members attribs
foreach (Attribute attr in m.GetCustomAttributes())
{
if (attr.GetType() == typeof(EnsureIndex))
indexAttr = (EnsureIndex)attr;
if (attr.GetType() == typeof(RepoElementAttribute))
elementNameOverride.Add(((RepoElementAttribute)attr).ElementName);
if ((indexAttr != null) && (elementNameOverride.Count != 0))
break;
}
// Index
if (indexAttr != null)
{
if (elementNameOverride.Count() > 0)
EnsureIndexesAsDeclared(indexAttr, elementNameOverride);
else
EnsureIndexesAsDeclared(indexAttr);
}
}
// Walk the atributes on the class itself. WARNING: We don't validate the member names here, we just create the indexes
// so if you create a unique index and don't have a field to match you'll get an exception as you try to add the second
// item with a null value on that key
foreach (Attribute attr in theClass.GetCustomAttributes(true))
{
if (attr.GetType() == typeof(EnsureIndexes))
EnsureIndexesAsDeclared((EnsureIndexes)attr);
}//foreach
}//if this.collection.count
}//AssureIndexesNotInlinable()
EnsureIndexes then looks like this;
private void EnsureIndexesAsDeclared(EnsureIndexes attr, List<string> indexFields = null)
{
var eia = attr as EnsureIndexes;
if (indexFields == null)
indexFields = eia.Keys.ToList();
// use driver specific methods to actually create this index on the collection
var db = GetRepositoryManager(); // if you have a repository or some other method of your own
db.EnsureIndexes(indexFields, attr.Descending, attr.Unique, attr.Sparse);
}//EnsureIndexes()
Note that you'll place this after each and every update because if you forget somewhere your indexes may not get created. It's important to ensure therefore that you optimise the call so that it returns quickly if there's no indexing to do before going through all that reflection code. Ideally, you'd do this just once, or at the very least, once per application startup. So one way would be to use a static flag to track whether you've already done so, and you'd need additional lock protection around that, but over-simplistically, it looks something like this;
void AssureIndexes()
{
if (_requiresIndexing)
AssureIndexesInit();
}
So that's the method you'll want in each and every DB update you make, which, if you're lucky would get inlined by the JIT optimizer as well.
See below for a naive implementation which could do with some brains to take the indexing advice from the MongoDb documentation into consideration. Creating indexes based on queries used within the application instead of adding custom attributes to properties might be another option.
using System;
using System.Reflection;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using NUnit.Framework;
using SharpTestsEx;
namespace Mongeek
{
[TestFixture]
class TestDecorateToEnsureIndex
{
[Test]
public void ShouldIndexPropertyWithEnsureIndexAttribute()
{
var server = MongoServer.Create("mongodb://localhost");
var db = server.GetDatabase("IndexTest");
var boatCollection = db.GetCollection<Boat>("Boats");
boatCollection.DropAllIndexes();
var indexer = new Indexer();
indexer.EnsureThat(boatCollection).HasIndexesNeededBy<Boat>();
boatCollection.IndexExists(new[] { "Name" }).Should().Be.True();
}
}
internal class Indexer
{
private MongoCollection _mongoCollection;
public Indexer EnsureThat(MongoCollection mongoCollection)
{
_mongoCollection = mongoCollection;
return this;
}
public Indexer HasIndexesNeededBy<T>()
{
Type t = typeof (T);
foreach(PropertyInfo prop in t.GetProperties() )
{
if (Attribute.IsDefined(prop, typeof (EnsureIndexAttribute)))
{
_mongoCollection.EnsureIndex(new[] {prop.Name});
}
}
return this;
}
}
internal class Boat
{
public Boat(Guid id)
{
Id = id;
}
[BsonId]
public Guid Id { get; private set; }
public int Length { get; set; }
[EnsureIndex]
public string Name { get; set; }
}
internal class EnsureIndexAttribute : Attribute
{
}
}

Entity Framework 4.1 bug in ObjectContext.SavingChanges handling (?)

I have a problem with something that seems to be a bug in Entity Framework 4.1: I have added a handler on ObjectContext.SavingChanges which updates a property "LastModified" whenever an object is added to or modified in the database. Then I do the following:
Add two objects to the database, and submit (call SaveChanges())
Modify the first object that was added
Extract the two objects ordered by LastModified
The resulting objects are returned in the wrong order. Looking at the objects, I can see that the LastModified property has been updated. In other words, the SavingChanges event was fired properly. But looking in the database, the LastModified column has not been changed. That is, there is now a difference between EF's cached objects and the rows in the database.
I tried performing the same update to LastModified in an overridden "SaveChanges" method:
public override int SaveChanges()
{
SaveChangesHandler();//updating LastModified property on all objects
return base.SaveChanges();
}
Doing this caused the database to be updated properly and the queries returned the objects in proper order.
Here is an entire test program showing the error:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Reflection;
using System.Threading;
namespace TestApplication
{
class Program
{
private PersistenceContext context;
private static void Main(string[] args)
{
var program = new Program();
program.Test();
}
public void Test()
{
SetUpDatabase();
var order1 = new Order {Name = "Order1"};
context.Orders.Add(order1);
var order2 = new Order {Name = "Order2"};
context.Orders.Add(order2);
context.SaveChanges();
Thread.Sleep(1000);
order1 = GetOrder(order1.Id); // Modified 1.
order1.Name = "modified order1";
context.SaveChanges();
List<Order> orders = GetOldestOrders(1);
AssertEquals(orders.First().Id, order2.Id);//works fine - this was the oldest object from the beginning
Thread.Sleep(1000);
order2 = GetOrder(order2.Id); // Modified 2.
order2.Name = "modified order2";
context.SaveChanges();
orders = GetOldestOrders(1);
AssertEquals(orders.First().Id, order1.Id);//FAILS - proves that the database is not updated with timestamps
}
private void AssertEquals(long id1, long id2)
{
if (id1 != id2) throw new Exception(id1 + " != " + id2);
}
private Order GetOrder(long id)
{
return context.Orders.Find(id);
}
public List<Order> GetOldestOrders(int max)
{
return context.Orders.OrderBy(order => order.LastModified).Take(max).ToList();
}
public void SetUpDatabase()
{
//Strategy for always recreating the DB every time the app is run.
var dropCreateDatabaseAlways = new DropCreateDatabaseAlways<PersistenceContext>();
context = new PersistenceContext();
dropCreateDatabaseAlways.InitializeDatabase(context);
}
}
////////////////////////////////////////////////
public class Order
{
public virtual long Id { get; set; }
public virtual DateTimeOffset LastModified { get; set; }
public virtual string Name { get; set; }
}
////////////////////////////////////////////////
public class PersistenceContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public PersistenceContext()
{
Init();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
public void Init()
{
((IObjectContextAdapter) this).ObjectContext.SavingChanges += SavingChangesHandler;
Configuration.LazyLoadingEnabled = true;
}
private void SavingChangesHandler(object sender, EventArgs e)
{
DateTimeOffset now = DateTimeOffset.Now;
foreach (DbEntityEntry entry in ChangeTracker.Entries()
.Where(entity => entity.State == EntityState.Added || entity.State == EntityState.Modified))
{
SetModifiedDate(now, entry);
}
}
private static void SetModifiedDate(DateTimeOffset now, DbEntityEntry modifiedEntity)
{
if (modifiedEntity.Entity == null)
{
return;
}
PropertyInfo propertyInfo = modifiedEntity.Entity.GetType().GetProperty("LastModified");
if (propertyInfo != null)
{
propertyInfo.SetValue(modifiedEntity.Entity, now, null);
}
}
}
}
I should add that the SavingChanges handler worked fine before we upgraded to EF4.1 and using Code-First (that is, it worked in EF4.0 with model-first)
The question is: Have I found a bug here, or have I done something wrong?
I'm not sure if this can be considered a Bug. What seems to happen is that the way you manipulate the LastModified property does not trigger INotifyPropertyChanged and thus the changes do not get populated to your Database.
To prove it use:
order2.Name = "modified order2";
((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetObjectStateEntry(order2).SetModifiedProperty("LastModified");
To utilize this knowledge in your SavingChangesHandler:
private void SavingChangesHandler(object sender, EventArgs e)
{
DateTimeOffset now = DateTimeOffset.Now;
foreach (DbEntityEntry entry in ChangeTracker.Entries()
.Where(entity => entity.State == EntityState.Added || entity.State == EntityState.Modified))
{
SetModifiedDate(now, entry);
if (entry.State == EntityState.Modified)
{
((IObjectContextAdapter) this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity).SetModifiedProperty("LastModified");
}
}
}
Edit:
I looked into this a little more and you are correct. For some reason MS decided to not fire PropertyChanged events when using PropertyInfo.SetValue anymore. Only one way to find out if this is a bug or a design decision: File a bug report / Post to msdn Forums.
Though changing the property directly via CurrentValue seems to work fine:
private static void SetModifiedDate(DateTimeOffset now, DbEntityEntry modifiedEntity)
{
if (modifiedEntity.Entity == null)
{
return;
}
modifiedEntity.Property("LastModified").CurrentValue = now;
}

How can you generically map a DbDataReader to a Castle.Windsor resolved type?

This is confusing me, so this question will probably be confusing.
I have a an application that uses implementations of an IJob interface to accomplish different tasks.
public interface IJob
{
int Id { get; set; }
string Name { get; set; }
void Run();
}
I am using the Castle.Windsor.WindsorContainer to resolve these implementations, and using the service id to help identify them.
WindsorContainer container = new WindsorContainer(new XmlInterpreter());
IJob jobToExecute = container.Resolve<IJob>("nameOfJob");
I wrote a little generic extension method that simply puts the values of SQL columns into their corresponding properties.
public static void MapTo<T>(this DbDataReader reader, ref T instance) where T : class
{
Type objectType = typeof(T);
foreach (PropertyInfo propertyInfo in objectType.GetProperties())
{
if (propertyInfo.CanWrite)
{
int ordinal = -1;
try
{
ordinal = reader.GetOrdinal(propertyInfo.Name);
object value = reader[ordinal] == DBNull.Value ? null : reader[ordinal];
propertyInfo.SetValue(instance, value, null);
}
catch (IndexOutOfRangeException ex)
{
continue;
}
}
}
}
Now, because you can't instantiate an instance of an interface, passing an IJob to this method won't work. However, in order to gain the benefits of the IoC container, I need to do everything in my repository using the IJob interface. So, I wrote with this to resolve the IJob implementation, and pass it to the MapTo method to populate the necessary properties:
public IJob GetJobById(int id)
{
string cmdTxt = "SELECT Id, Name, Description, DateStarted, ScheduledCompletion, Completed FROM Jobs WHERE Id = #id";
using (DbCommand cmd = _dataFactory.CreateCommand(cmdTxt))
{
_dataFactory.AddParam(cmd, "id", id, DbType.Int32);
using (DbDataReader rdr = cmd.ExecuteReader())
{
if (rdr.Read())
{
IJob job = _container.Resolve<IJob>("job.implementation");
rdr.MapTo<IJob>(ref job);
return job;
}
else
{
return null;
}
}
}
}
Is this an OK design decision? Do you see any problems?
Well, for one, calling methods via reflection is usually not nice... and it looks like you're using Windsor as a type dictionary, which it is not...
I would write a non-generic MapTo (which would take a Type as parameter) that operates on an already-existing instance (when you create a new instance with Activator.CreateInstance you discard the instance Windsor had resolved) and then use it from the ComponentCreatedEvent event in IKernel. Something like this:
container.Kernel.ComponentCreated += (model, instance) => {
if (model.Service == typeof(IJob)) {
// select id,name from jobs where id = model.Name
// use MapTo to fill id,name into instance
}
}