how to insert embedded document using spring data mongodb mongotemplate - spring

I need to insert a new track into the existing event document following is my class structure
class Event
{
String _id;
List<Track> tracks;
}
class Track
{
String _id;
String title;
}
My existing document is
{
"_id":"1000",
"event_name":"Some Name"
}
document will look like after insertion
{
"_id":"1000",
"event_name":"Some name",
"tracks":
[
{
"title":"Test titile",
}
]
}
How can i insert that track into my existing document using mongoTemplate spring data mongodb?

First, you have to annotate Event class with #Document:
#Document(collection = "events")
public class Event
{
// rest of code
}
The code for adding an event should look like this:
#Repository
public class EventsDao {
#Autowired
MongoOperations template;
public void addTrack(Track t) {
Event e = template.findOne
(new Query(Criteria.where("id").is("1000")), Event.class);
if (e != null) {
e.getTracks().add(t);
template.save(e);
}
}
}
Note : You should change Event's class String _id; to String id; in order for this example to work (or change the query literal).
Edit update a track is also fairly easy. Suppose you want to change the first track's title:
Event e = template.findOne(new Query(Criteria.where("_id").is("1000")), Event.class);
if (e != null) {
e.getTracks().get(0).setTitle("when i'm 64");
template.save(e);
}

Related

How to implement the custom converter for a field using Spring Data Elasticsearch?

I need to implement a custom converter in spring data elastic-search. I need to concatenate something to the text when saving and retrieving. I have seen a similar question is here. And in the answer is says it is implemented now and available. But I didn't found any example how to implement it. So anybody know how to do this?
You can find examples in the test code of the library.
You have to create a converter:
class FooConverter implements PropertyValueConverter {
public static final String PREFIX = "foo-";
#Override
public Object write(Object value) {
return PREFIX + value.toString();
}
#Override
public Object read(Object value) {
String valueString = value.toString();
if (valueString.startsWith(PREFIX)) {
return valueString.substring(PREFIX.length());
} else {
return value;
}
}
}
and then register it for the property of your entity class:
#Document(indexName = "foo")
class Entity {
#Field(type = FieldType.Text)
#ValueConverter(FooConverter.class)
private String someField;
// ...
}

How to achieve row level authorization in spring-boot?

Assuming I've the following endpoints in spring boot
GET /todo
DELETE /todo/{id}
How can ensure that only entries for the userid are returned and that the user can only update his own todos?
I've a populated Authentication object.
Is there any build in way I can use? Or just make sure to always call findXyzByIdAndUserId where userid is always retrieved from the Principal?
I'm a bit worried about the possibility to forget the check and displaying entries from other users.
My approach to this would be a 3 way implementation: (using jpa & hibernate)
a user request context
a mapped superclass to get your context
a statement inspector to inject your userid
For example:
public final class UserRequestContext {
public static String getUserId() {
// code to retrieve your userid and throw when there is none!
if (userId == null) throw new IllegalStateException("userid null");
return userId;
}
}
#MappedSuperclass
public class UserResolver {
public static final String USER_RESOLVER = "USER_RESOLVER";
#Access(AccessType.PROPERTY)
public String getUserId() {
return UserRequestContext.getUserId();
}
}
#Component
public class UserInspector implements StatementInspector {
#Override
public String inspect(String statement) {
if (statement.contains(UserResolver.USER_RESOLVER)) {
statement = statement.replace(UserResolver.USER_RESOLVER, "userId = '" + UserRequestContext.getUserId() + "'" );
}
return sql;
}
#Bean
public HibernatePropertyCustomizer hibernatePropertyCustomizer() {
return hibernateProperies -> hibernateProperties.put("hibernate.session_factory.statement_inspector",
UserInspector.class.getName());
}
}
So your Entity looks like this:
#Entity
...
#Where(clause = UserResolver.USER_RESOLVER)
public class Todo extends UserResolver {
...
}

Recursive Select in Entity Framework

I have a class Activity that can have several activities associated with it (List). How can I configure my class with Fluent API to load all other child activities if one parent activity is selected?
Here is my Domain Class for Activity:
public class Activity : ProjectBase
{
private string activityType;
public string ActivityType
{
get { return activityType; }
set { activityType = value; }
}
private string catagory;
public string Catagory
{
get { return catagory; }
set { catagory = value; }
}
private string priority;
public string Priority
{
get { return priority; }
set { priority = value; }
}
public Activity()
:base()
{
}
}
ProjectBase has the List property declared. My database is generated following Table Per Hierarchy and the table for Activity seems generated fine for recursion.
Any suggestion is highly appreciated.
All I had to do was put virtual on the list and it brought all related records:
private List<Activity> activities = new List<Activity>();
public virtual List<Activity> Activities
{
get { return activities; }
set { activities = value; }
}
Thanks for the help people!

Repository Pattern and Azure Table Storage(???)

While doing the following simple example, I found the following difficulties
As the title says, I am intending to use the Repository pattern while I am storing data in the Azure table storage.now I have couple of classes, Repository.cs, IRepository.cs, DataContext.cs and the Controller.
During my reading I found some info and been doing as follows.
IRepository.cs
public interface IRepository<T> where T: TableServiceEntity
{
T GetById(int Id);
IQueryable<T> GetAll();
}
and the DataContext.cs
public class DataContext<T>:TableServiceContext where T:TableServiceEntity
{
public DataContext(CloudStorageAccount storageaccount, StorageCredentials credentials)
: base(storageaccount.TableEndpoint.AbsoluteUri, credentials)
{
// _storageAccount = storageaccount;
var storageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue(KEY_STORAGE));
storageAccount.CreateCloudTableClient().CreateTableIfNotExist(tableName);
}
public IQueryable<T> DeviceTable
{
get { return CreateQuery<T>(tableName); }
}
}
plus some part of the controller(I have already data in the table which I created before)
public class DeviceMeController : Controller
{
private IRepository<Entity>_repository;
public Controller() : this(new Repository<Entity>())
{
}
public Controller(IRepository<Entity> repository)
{
_repository = repository;
}
public ActionResult Index()
{
var List = _repository.GetAll();
return View(deviceList);
}
and the the Implementation of the interface Reposistory.cs, here is where I have an error and got lost somewhere
public class Repository<T>:IRepository<T> where T:TableServiceEntity
{
private DataContext<T> _serviceContext;
// here get tablename as pararameter;
// so the Enities call this function
public Repository()
{
// the same context for multiple tables ?
}
// perhaps it should take the table Name
public void Add(T item)
{
_serviceContext.AddObject(TableName,item);
_serviceContext.SaveChangesWithRetries();
}
public IQueryable<T> GetAll()
{
var results = from c in _serviceContext.Table
select c;
return results;
Error is about the null reference, the debugger shows the variable results is null?
In the end I need to know few things.
what should I do in the Repository.cs constructor? I believe the Datacontext.cs class has to be in a separate class ...
any Hint here
Hy,
first of all I presume you left out some code, because I don't see how you get your context in your repository. But supposing you do set it correctly, (injection?) taking into account the way you desinged your datacontext the repository doesn't need to know the table name because it is set in the following lines of code:
public IQueryable<T> DeviceTable
{
get { return CreateQuery<T>(Constants.DeviceTableName); }
}
So when you create a query based on the IQueryable DeviceTable, the table name is already set.
The thing is I don't see the need for your context class, especially as it can only bring over a single entity type (it is generic and based on an entity).
A basic layout of my Repository for Azure Table Storage is:
public abstract class CloudRepository<TEntity> : ICloudRepository<TEntity>
{
private TableServiceContext _tableServiceContext;
private string _tableName;
public string TableName
{
get { return _tableName ?? ( _tableName = typeof(TEntity).Name.Replace("Entity", string.Empty).ToLower()); }
}
public CloudStorageAccount StorageAccount
{
get
{
return CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString"));
}
}
public CloudTableClient TableClient
{
get
{
CloudTableClient cloudTableClient = StorageAccount.CreateCloudTableClient();
cloudTableClient.CreateTableIfNotExist(TableName);
return cloudTableClient;
}
}
public TableServiceContext ServiceContext
{
get
{
return _tableServiceContext ?? (_tableServiceContext = TableClient.GetDataServiceContext());
}
}
public IEnumerable<TEntity> FindAll()
{
return ServiceContext.CreateQuery<TEntity>(TableName).ToList();
}
}
Hope this helps you.

Neo4j eager/lazy loading with Spring data

I'm investigating Neo4j and have a question with regards to object eager/lazy loading. Lets say I have class Trolley with has Set<Item> (with getters/setters). If I do the following:
Trolley t = new Trolley(...); // create empty trolley
t.addItem(f); // add one item to the trolley
t.persist(); // persist the object
I then later find the trolley based on the nodeId:
repo.findOne(xxx); // returns the trolley successfully
When I do something like:
trolley.getItems().size()
this is empty. I guess this is the intended behaviour. Is there any mechanism similar to JPA where is the session/tx is open to load the collection dynamically.
Code:
#NodeEntity
public class Trolley
{
#Indexed
private String name;
#RelatedTo
private Set<Item> items;
public Trolley(){}
public Trolley(String name)
{
this.name = name;
}
public void addItem(Item item)
{
this.items.add(item);
}
public Set<Item> getItems()
{
return items;
}
}
#NodeEntity
public class Item
{
private String name;
public Item(){}
public Item(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
}
#Test
public void trolleyWithItemPersist()
{
Trolley trolley = new Trolley("trolley1").persist();
// Persisting - however I would've expected a cascade to
// occur when adding to the collection.
Item item = new Item("item1").persist();
// now add to the trolley
trolley.addItem(item);
// persist
trolley.persist();
// Now use repo to get trolley
Trolley loadedTrolley = trolleyRepository.findOne(trolley.getNodeId());
// should have one item
assertEquals(1, loadedTrolley.getItems().size());
}
Afaik, in Spring Data Jpa, to populate an lazy loaded field you need to annotate the method which call the findOne(xxx) with
#Transactional
from (org.springframework.transaction.annotation.Transactional)
Maybe it works also with neo4j...
I'm not really an skilled developper on Spring Data but this is the only way I know to retrieve lazy loaded fields. If someone has a better solution, feel free to write it!

Resources