This is my first time using MongoDb and morphia and I am pretty new to databases in general. I am wondering how I should organize my code with morphia. I was looking into using a DAO like it says on the morphia documentation, but the way they seem to be doing it, I would have to create a DAO for each model object that I have. I liked play's methodology of basically giving Model objects the ability to save themselves but I only have vague notions of what is going on under the hood here, so I am not sure how to achieve this with morphia, or if it is even desirable to do so. The code I have so far looks like this for the skeleton of a User model.
#Entity("user")
public class User extends BasicDAO<User, ObjectId>{
#Id ObjectId id;
public String firstName;
public String lastName;
public String email;
#Indexed public String username;
public String password;
public User(Mongo mongo, Morphia morphia){
super(mongo, morphia, "UserDAO");
}
public User(){
this(DBFactory.getMongo(), DBFactory.getMorphia());
}
public void save(){
ds.save(this);
}
public static User findByUsername(String uname){
return DBFactory.getDatastore().find(User.class, "username =", uname).get();
}
public static boolean authenticate(String uname, String pword){
User user = DBFactory.getDatastore().createQuery(User.class).filter("username", uname).filter("password", pword).get();
if(user == null)
return false;
else
return true;
}
}
It is currently throwing a StackOverflowException, and I am not sure why, but is this a reasonable pattern to try to accomplish?
Also the DBFactory basically just exists to maintain the singleton mongodb connection.
Play 2.0 have a module for working with MongoDb I think You should give it a try
https://github.com/vznet/play-mongo-jackson-mapper#readme
I started using Marphia with play framework 2.x. In my opinion, it is more sophisticated than the jackson mapper. I followed this example to install marphia plugin: https://github.com/czihong/playMongoDemo
Related
I have a SpringBoot 2.6.11 application with JPA 2.2.
I have an entity like this:
#Data
#Entity
#Table(name = "entity")
public class Entity implements Serializable {
....
#Convert(converter = ListConverter.class)
private List<String> referenceCode;
....
}
I have this Converter:
#Converter(autoApply = true)
public class ListConverter implements AttributeConverter<List<String>, String> {
#Override
public String convertToDatabaseColumn(List<String> attribute) {
return String.join(";", attribute);
}
#Override
public List<String> convertToEntityAttribute(String dbData) {
return new ArrayList<>(Arrays.asList(dbData.split(";")));
}
}
And when I insert or extract this element all working fine. But now I wanna query that element and I don't know how to do it. If I do something like that:
public List<Entity> findByReferenceCode(String reference);
It doesn't work, if I do:
#Query("select e from Entity e where e.referenceCode IN ?1")
public List<Entity> findByReferenceCode(List<String> reference);
Still doesn't work..
The only way I found is by the nativeQuery but is really an extrema ratio. Ho can I solve this?
Thank you
To really do what you want here, you need to use an #ElementCollection. The reason being that there is no reliable way for JPA to query a single column and treat it as a collection. Reliably querying a collection requires a second table (which is what #ElementCollection does). You can continue to use the #Converter, but your queries will have to be customized to handle the disparity between the entity attribute type (list) and the actual database column type (string).
If you are okay with the limitations of the #Converter then it's fine (I have used them this way) but if you truly need to query the attribute like a collection (e.g. search for multiple independent items, perform counts, aggregations, etc) and you want those queries to be generated by a JPA layer, then you will have to use #ElementCollection and let it create a second table.
I need help to get the data from another document I have the following class.
#Data
#Document(collection = "tmVersion")
public class TmVersion {
#Id
private String id;
private String cVrVersionId;
#DBRef
private TaApplicationVersion taApplicationVersion;
}
and
#Data
#Document(collection = "taApplicationVersion")
public class TaApplicationVersion {
#Id
private String id;
private String dVrAppName;
private String dVrAppCode;
}
This is my repository in which I map what I want to be shown but in taApplicationVersion I need to show all this object also how is it done?
#Query(value="{}", fields="{'cVrVersionId': 1, 'taApplicationVersion.dVrAppName': 2,
'dVrVersionNumber': 3}")
Page<TmVersion> getAllVersionWithOutFile(Pageable pageable)
Couple of things to mention here.
If you want this kind of join between tables, then you need to rethink your choice of Mongodb as database. No Sql Databases thrive on the fact that there is very less coupling between tables(collections). So if you are using #DBRef, it negates that. Mongodb themselves do not recommend using #DBRef.
This cannot be achieved with the method like you have in the repository. You need to use Projections. Here is the documentation for that.
Create a Porjection interface like this. Here you can control which fields you need to include in the Main class(TmVersion)
#ProjectedPayload
public interface TmVersionProjection {
#Value("#{#taApplicationVersionRepository.findById(target.taApplicationVersion.id)}")
public TaApplicationVersion getTaApplicationVersion();
public String getId();
public String getcVrVersionId();
}
Change the TmVersionRepository like this
public interface TmVersionRepository extends MongoRepository<TmVersion, String> {
#Query(value="{}")
Page<TmVersionProjection> getAllVersionWithOutFile(Pageable pageable);
}
Create a new Repository for TaApplicationVersion. You can add #Query on top of this method and control which fields from subclass needs to be returned.
public interface TaApplicationVersionRepository extends MongoRepository<TaApplicationVersion, String> {
TaApplicationVersion findById(String id);
}
I am trying to enable Auditing using Annotations. My domain class has #Id field that is populated while constructing the object. I have added a java.util.Date field for lastModified and annotated it with #LastModifiedDate.
#Document
public class Book {
#Id
private String name;
private String isbn;
#LastModifiedDate
private Date lastModified;
public Book(String name) {
this.name = name;
}
}
I have enabled auditing in the Spring Configuration XML using <mongo:auditing/>.
When I try to save an instance of my object, I get the following error:
Book book1 = new Book("ABCD");
mongoOps.save(book1);
java.lang.IllegalArgumentException: Unsupported entity com.pankaj.Book! Could not determine IsNewStrategy.
I do not want to use the Auditable interface nor extend my domain classes from AbstractAuditable. I only want to use the Annotations.
Since I am not interested in the #CreatedBy and the #LastModifiedBy, I am not implementing the AuditAware interface as well.
I just want the #LastModifiedDate to work for my domain classes. What am I missing?
I am using version 1.7.0 of SpringData MongoDB.
You don't mention how you are configuring your MongoDB connection but if you are using AbstractMongoConfiguration, it will use the package of the actual configuration class to look for #Document annotated classes at startup.
If your entities are in a different package, you will have to manually hand that package by overriding AbstractMongoConfiguration.getMappingBasePackage(). Placing this in you Mongo Configuration class should do the trick (again, this is considering you are extending AbstractMongoConfiguration for your Mongo configuration):
#Override
protected String getMappingBasePackage() {
return "package.with.my.domain.classes";
}
I had same issue, later I determined that I was missing ID field with annotation;
#Id
private String Id
in my class I was trying to persist with
#Document(collection="collectionName")
I had the same issue when using annotations only configuration.
When you put #EnableMongoAuditing on a configuration class, Spring will create a MappingContext bean.
Then you have to make sure the same mappingContext is being used in the MongoTemplate.
#Configuration
#EnableMongoAuditing
#EnableMongoRepositories(value = "my.repositories.package", mongoTemplateRef = "myMongoTemplate")
class MongoConfig {
#Autowired
//Autowiring the MongoMappingContext will supply the same MongoMappingContext as the one used in auditing
MongoMappingContext mongoMappingContext;
#Bean
MongoTemplate myMongoTemplate() {
String databaseName = "mydbname";
MongoDbFactory factory = new SimpleMongoDbFactory(mongoClient, databaseName);
MongoConverter converter = new MappingMongoConverter(factory, mongoMappingContext);
MongoTemplate mongoTemplate = new MongoTemplate(factory, converter);
return mongoTemplate;
}
}
My project running in version 1.6.2 runs normally, except that # LastModifiedDate does not update. After I updated to version 1.7.1. I had the same problem as you.
I tried to implement the class: org. Springframework. Data. Domain. The Auditable this interface, seemingly can preserve the normal, but the createdBy and createdDate two fields could not be saved to the database.
I had the same issue and fixed it by extending the Document class with AbstractPersistable. In you case it can be
public class Book extends AbstractAuditable
I'm trying to use JPA in Play Framework for Java version 2.3.7.
Before in Play 1.x, there was a Model superclass that made it really easy to execute queries like "List persons = Person.findAll();".
Is there Model superclass for javaJpa to do this?
There is no play.db.jpa.Model class for Play 2
But you can use play.db.jpa.JPA
and to find all do
JPA.em().createQuery("select p from Person p").getResultList();
where the create query contains JPQL and Person is entity name .
For more details check sample/computer-database-jpa.
Also check Play Docs,Similar
I think there's no play.db.jpa.Model on play 2.
The closest thing should be Ebean and SpringJPA which I use and recommend because of Ebean being soon removed in favor of JPA and being JPA mature and well documented.
As a quick example, those should look like:
Ebean
FindAllUsage
List<Person> people = Person.find.all();
Person model
#Entity
public class Person extends Model
{
#Id
public Long id;
public String value;
public static final Model.Finder<Long, UserPermission> find =
new Model.Finder<Long, UserPermission>(Long.class,UserPermission.class);
}
SpringJPA
FindAllUsage
List<Person> people = personRepository.findAll();
Person repository
#Named
#Singleton
public interface PersonRepository extends CrudRepository<Agent,Long> {
}
Person model
#Entity
public class Person
{
#Id
public Long id;
public String value;
}
In Play 2 the Model class is extending Ebean ORM by default and it has these general methods as save, update, find.byId, find.all etc.
I have a model class Alert with a one-to-many relationship with another model class Occurrence, as follows:
#Entity public class Alert extends Model
{
public String name;
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="alert")
public List<Occurrence> occurrences;
}
#Entity public class Occurrence extends Model
{
#ManyToOne
public Alert alert;
#Column(nullable=false)
public Date alertTime;
}
I have a view that lists Alerts in a simple table, which should have a column labelled "Occurrences today", with a count of how many occurrences of the alert have happened today (by alertTime).
I can't seem to find a way to do this using only JPA/Hibernate annotations in the Alert model class, and since I am listing Alerts, I don't know of a clean way to include the count in each Alert object.
So now I am wondering if it would be fine to simply query for the alert's occurrences from within the Alert model class itself, like so:
#Entity public class Alert extends Model
{
public String name;
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="alert")
public List<Occurrence> occurrences;
#Transient
public long getOccurrencesToday()
{
return Occurrence.count(
"alert = ? and alertTime >= ?",
this, new DateMidnight().toDate());
}
}
My question is: Is it considered bad form to make queries to the DB from within a model class?
A secondary question is: Am I approaching this the wrong way? Is there a better way to achieve the end result that I've missed?
making queries from within domain model class should be the correct way to go giving those queries are relevant to that model. To this end, I don't see any problem with your code.
From Play's philosophy, domain object is not merely a data object. It should enclose certain business logic inside. In fact pure data object is not encouraged by play. Check more on http://www.playframework.org/documentation/1.2.4/model
While your approach is tempting, its dangerous because effectively you're putting logic into a "getter" (getOccurrencesToday()) which you'll probably access by ${alert.occurrencesToday}
A getter should always be side effect free.
I would change this to
#Entity public class Alert extends Model
{
public String name;
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="alert")
public List<Occurrence> occurrences;
#Transient public Long occurrencesToday = null;
public void populateOccurrencesToday()
{
occurrencesToday = Occurrence.count(
"alert = ? and alertTime >= ?",
this, new DateMidnight().toDate());
}
}
then in your controller, iterate over all alerts that
you're going to render and execute the populateOccurrencesToday() method.
If you're now storing your object in a cache, its data will be consistent.