EclipseLink/JPA2 ManyToOne bidirectional cascade persist issue - jpa-2.0

This is similar to How to cascade persist using JPA/EclipseLink
I have to entities like so. One is RoomEntity that has a one to many bi-directional relationship with ComputerEntity. eg. each room has 0..n computers.
#Entity
public class ComputerEntity implements Serializable {
#Id
#GeneratedValue(generator="computerSeq",strategy= GenerationType.SEQUENCE)
#SequenceGenerator(name="computerSeq",sequenceName="SEQUENCECOMPUTERID",allocationSize=1)
private long computerID;
#Column(name = "COMPUTERID")
public long getComputerID() {
return computerID;
}
public void setComputerID(long computerID) {
this.computerID = computerID;
}
private RoomEntity room;
#ManyToOne()
#JoinColumn(name = "ROOMID", referencedColumnName = "ROOMID")
public RoomEntity getRoom() {
return room;
}
public void setRoom(RoomEntity room) {
this.room = room;
}
} //ComputerEntity
#Entity
public class RoomEntity {
#Id
#GeneratedValue(generator="roomSeq",strategy= GenerationType.SEQUENCE)
#SequenceGenerator(name="roomSeq",sequenceName="SEQUENCEROOMID",allocationSize=1)
private long roomID;
#OneToMany(mappedBy = "room", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private Set<ComputerEntity> computers;
#javax.persistence.Column(name = "ROOMID")
public long getRoomID() {
return roomID;
}
public void setRoomID(long roomID) {
this.roomID = roomID;
}
public Set<ComputerEntity> getComputers() {
return computers;
}
public void setComputers(Set<ComputerEntity> computers) {
for(ComputerEntity computer : computers) {
computer.setRoom(this);
}
this.computers = computers;
}
}//RoomEntity
When I try to persist an new room with a computer like so:
RoomEntity room = new RoomEntity();
room.setAdministrator("Fox Moulder");
room.setLocation("Area 51");
ComputerEntity computer1 = new ComputerEntity();
computer1.setDescription("Alienware area51 laptop");
Set<ComputerEntity> computers = new HashSet<ComputerEntity>();
computers.add(computer1);
room.setComputers(computers);
roomBean.createRoom(room);
roomBean is a Stateless EJB and roomBean.createRoom simply calls entityManager.persist(room). Since I have a CascadeType.PERSIST on the computers field of RoomEntity the ComptuerEntity is created. However, if I look at the room field of that ComputerEntity I see that room field is null. I would have assumed that Eclipselink would have automatically filled in the room since I have a bi-directional relationship. In order to have the room set in this way I had to add
for(ComputerEntity computer : computers) {
computer.setRoom(this);
}
to room.setComputers(...). Is this the correct approach or is there a way to get Eclipselink to automatically set this?
Thanks.
-Noah

There is no relationship maintenance in JPA. EJB 2.1 has shown that generic relationship management is for too inefficient and cumbersome.
I recommend adding an addComputer() method that would set the RoomEntity on the newly added ComputerEntity

Related

How to test an Update class in apex

Hello I am new to apex and soql, I would like some help on my test class, below is the code i am having trouble with,
public class UpdateMyCard {
#AuraEnabled(cacheable=false)
public static Card__c updateCard(Double Amount){
String userId = UserInfo.getUserId();
Card__c myCard = [SELECT Id, Card_no__c, total_spend__c From Card__c Where OwnerId =:userId];
myCard.total_spend__c = myCard.total_spend__c + Amount;
try{
update myCard;
}catch (Exception e) {
System.debug('unable to update the record due to'+e.getMessage());
}
return myCard;
}
}
Test Class
#isTest
public static void updatecard(){
UpdateMyCard.updateCard(200);
}
Error:
System.QueryException: List has no rows for assignment to SObject
Your code assumes there's already a Card record for this user. And it's exactly 1 record (if you have 2 your query will fail). I'm not sure what's your business requirement. Is it guaranteed there will always be such record? Should the code try to create one on the fly if none found? You might have to make that "updateCard" bit more error-resistant. Is the total spend field marked as required? Because "null + 5 = exception"
But anyway - to fix your problem you need something like that in the test.
#isTest
public static void updatecard(){
Card__c = new Card__c(total_spend__c = 100);
insert c;
UpdateMyCard.updateCard(200);
c = [SELECT total_spend__c FROM Card__c WHERE Id = :c.Id];
System.assertEquals(300, c.total_spend__c);
}

How can I write UnitTest for service method with REQUIRES_NEW and constraint in DB

How can I create UnitTest for this method?
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Override
public Charge saveInNewTransaction(Charge charge) {
if (charge.getIsDuplicate()) {
charge.setIsActive(false);
} else {
Charge chargeOrigin = charge.getChargeOrigin();
chargeOrigin.setIsActive(false);
chargesRepository.saveAndFlush(chargeOrigin);
}
return chargesRepository.saveAndFlush(charge);
}
I have constraint - only one can be isActive = true
I have no idea how to write a test for this method.
brief background. I get a list of charges for example 10 and in a cycle I try to save them. if i get an constraint exception - I need ignore it(and continue save another charges. I do not need rolback main transaction).
therefore I have such a method which is executed in REQUIRES_NEW transaction.
EDIT
I trying:
#MockBean
private ChargeRepository chargeRepository;
#Before
public void setUp() throws Exception {
chargeService = new ChargeServiceImpl(chargeRepository);
}
#Test
public void saveInNewTransaction() {
Charge charge = new Charge();
charge.setIsDuplicate(true);
Charge originCharge = new Charge();
originCharge.setIsDuplicate(false);
charge.setChargeOrigin(originCharge);
//when(???).thenReturn(???);
Charge savedCharge = chargeService.saveInNewTransaction(testCharge);
//assertThat(???);
}

Acumatica - Adding Multiple Tracking Numbers in Field

I am adding the Tracking Number data field from SOPackageDetail to the Invoices screen (SO303000) on the Freight Details Tab. I know that it only shows one ShipmentNbr and this is what I'm using to join the two tables but I would like all of the tracking numbers, since there can be more than one per shipment number, to show in the field instead of just one. They can be just separated in the field value by a comma. Here is my code and it does work for just one tracking number.
Graph:
public class SOInvoiceEntry_Extension : PXGraphExtension<SOInvoiceEntry>
{
#region Event Handlers
protected void SOFreightDetail_RowSelecting(PXCache cache, PXRowSelectingEventArgs e, PXRowSelecting del)
{
if (del != null)
del(cache, e);
var row = (SOFreightDetail)e.Row;
if (row == null) return;
using (new PXConnectionScope())
{
SOPackageDetail track = PXSelect<SOPackageDetail, Where<SOPackageDetail.shipmentNbr, Equal<Required<SOFreightDetail.shipmentNbr>>>>.Select(Base, row.ShipmentNbr);
if(track != null){
SOFreightDetailExt invoiceExt = row.GetExtension<SOFreightDetailExt>();
if (invoiceExt != null){
invoiceExt.TrackNumber = track.TrackNumber;
}
}
}
}
#endregion
}
DAC Extension:
public class SOFreightDetailExt : PXCacheExtension<PX.Objects.SO.SOFreightDetail>
{
#region TrackNumber
public abstract class trackNumber : PX.Data.IBqlField
{
}
protected string _TrackNumber;
[PXString()]
[PXDefault()]
[PXUIField(DisplayName = "Tracking Number", IsReadOnly = true)]
public virtual string TrackNumber
{
get
{
return this._TrackNumber;
}
set
{
this._TrackNumber = value;
}
}
#endregion
}
I want all tracking numbers associated with the Shipment Nbr to be displayed in this field, right now it only shows one. This only will happen if there is multiple packages for one shipment number.
You need to loop on your records (PXSelect) in a foreach. You then need to add each string value to your tracknumber field. Something like this should work...
SOFreightDetailExt invoiceExt = row.GetExtension<SOFreightDetailExt>();
if(invoiceExt == null)
return;
foreach(SOPackageDetail track in PXSelect<SOPackageDetail, Where<SOPackageDetail.shipmentNbr, Equal<Required<SOFreightDetail.shipmentNbr>>>>.Select(Base, row.ShipmentNbr))
{
invoiceExt.TrackNumber = $"{invoiceExt.TrackNumber}, {track.TrackNumber}";
}
Also, there is no need for the PXConnectionScope. You can remove that.

How to refer PageReference method from apex test class

I am new to apex, I have created a button to call the apex class through the visual force page.
Here is my visual force page code.
<apex:page standardController="Opportunity"
extensions="myclass"
action="{!autoRun}">
</apex:page>
Here is my apex class.
public class myclass {
private final Opportunity o;
String tmp;
public myclass(ApexPages.StandardController stdController) {
this.o = (Opportunity)stdController.getRecord();
}
public PageReference autoRun() {
String theId = ApexPages.currentPage().getParameters().get('id');
for (Opportunity o:[select id, name, AccountId, from Opportunity where id =:theId]) {
//Create the Order
Order odr = new Order(
OpportunityId=o.id
,AccountId = o.AccountId
,Name = o.Name
,EffectiveDate=Date.today()
,Status='Draft'
);
insert odr;
tmp=odr.id;
}
PageReference pageRef = new PageReference('/' + tmp);
pageRef.setRedirect(true);
return pageRef;
}
}
I want to create test class. I don't know how to refer PageReference autoRun() method from test class. Guys need help if some one can tell me about test class of this apex class.
You will need to configure the StandardController for the inserted Opportunity. Then pass the StandardController to pass to the constructor before calling the method to test.
E.g.
public static testMethod void testAutoRun() {
Opportunity o = new Opportunity();
// TODO: Populate required Opportunity fields here
insert o;
PageReference pref = Page.YourVisualforcePage;
pref.getParameters().put('id', o.id);
Test.setCurrentPage(pref);
ApexPages.StandardController sc = new ApexPages.StandardController(o);
myclass mc = new myclass(sc);
PageReference result = mc.autoRun();
System.assertNotEquals(null, result);
}

Server architecture and SubSonic

Im trying to develop an server /client application. The server will be a bunch of webservices, the idea was to expose methods like:
Company GetNewCompany(); //Creates an new Company Object
Save(Company C);
CompanyCollection GetCompany(Query q);
Where Query object is part of Subsonic 2.1. But the problem is that SubSonic is not built for this, Have I missed something here? or is it just impossible to to use subsonic query language over SOAP?
This would have been great feature, becuase then it is really easy to make an application server using subsonic.
Br
Soren.
If you want to use subsonic v3 you can look at this issue that talks about IUpdatable:
http://code.google.com/p/subsonicthree/issues/detail?id=30
This will let you use ado data services somewhat painlessly. You use a DB constructor that take a URI argument. This probably won't be a part of v3 but you could make changes like this yourself.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WcfClientTest.NorthwindService;
namespace WcfClientTest
{
/// <summary>
/// Summary description for WcfTest
/// To run these tests, load this project, and somehow get a server running at the URI.
/// This can be done by updating the service reference to start the development server.
/// </summary>
[TestClass]
public class WcfTest
{
private string baseURI = "http://127.0.0.1:49649/Northwind.svc";
private DB ctx;
/// <summary>
/// Sets up test.
/// </summary>
[TestInitialize]
public void SetUp()
{
ctx = new DB(new Uri(baseURI));
}
[TestCleanup]
public void Cleanup()
{
}
[TestMethod]
public void Select_Simple_With_Variable()
{
int categoryID = 5;
IQueryable<Product> result = from p in ctx.Products
where p.CategoryID == categoryID
select p;
List<Product> products = result.ToList();
Assert.AreEqual(7, products.Count());
}
[TestMethod]
public void TestAddNew()
{
// add customer
var c = new Customer
{
CustomerID = "XXXXX",
ContactTitle = "Prez",
Country = "USA",
ContactName = "Big Guy",
CompanyName = "Big Guy Company"
};
ctx.AddToCustomers(c);
ctx.SaveChanges();
IQueryable<Customer> qCustomer = from cust in ctx.Customers
where cust.CustomerID == "XXXXX"
select cust;
Customer c2 = qCustomer.FirstOrDefault();
Assert.AreEqual("XXXXX", c2.CustomerID);
if (c2 != null)
{
ctx.DeleteObject(c2);
}
ctx.SaveChanges();
IQueryable<Customer> qCustomer2 = from cust in ctx.Customers
where cust.ContactName == "Big Guy"
select cust;
// Returns null if the row isn't found.
Customer c3 = qCustomer2.SingleOrDefault();
Assert.AreEqual(null, c3);
}
}
}
And this is all there is to the service:
using System.Data.Services;
using Northwind;
namespace NorthwindService
{
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults=false)]
public class Northwind: DataService<DB>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(IDataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.UseVerboseErrors = true;
}
}
}
And for web.config:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>